Auto merge of #6938 - Y-Nak:refactor-types, r=flip1995
Refactor types r? `@flip1995` This is the last PR to close #6724 🎉 Also, this fixes #6936. changelog: `vec_box`: Fix FN in `const` or `static` changelog: `linkedlist`: Fix FN in `const` or `static` changelog: `option_option`: Fix FN in `const` or `static`
This commit is contained in:
commit
2e33bf6347
173
clippy_lints/src/absurd_extreme_comparisons.rs
Normal file
173
clippy_lints/src/absurd_extreme_comparisons.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
|
||||
use clippy_utils::comparisons::{normalize_comparison, Rel};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_isize_or_usize;
|
||||
use clippy_utils::{clip, int_bits, unsext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for comparisons where one side of the relation is
|
||||
/// either the minimum or maximum value for its type and warns if it involves a
|
||||
/// case that is always true or always false. Only integer and boolean types are
|
||||
/// checked.
|
||||
///
|
||||
/// **Why is this bad?** An expression like `min <= x` may misleadingly imply
|
||||
/// that it is possible for `x` to be less than the minimum. Expressions like
|
||||
/// `max < x` are probably mistakes.
|
||||
///
|
||||
/// **Known problems:** For `usize` the size of the current compile target will
|
||||
/// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
|
||||
/// a comparison to detect target pointer width will trigger this lint. One can
|
||||
/// use `mem::sizeof` and compare its value or conditional compilation
|
||||
/// attributes
|
||||
/// like `#[cfg(target_pointer_width = "64")] ..` instead.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let vec: Vec<isize> = Vec::new();
|
||||
/// if vec.len() <= 0 {}
|
||||
/// if 100 > i32::MAX {}
|
||||
/// ```
|
||||
pub ABSURD_EXTREME_COMPARISONS,
|
||||
correctness,
|
||||
"a comparison with a maximum or minimum value that is always true or false"
|
||||
}
|
||||
|
||||
declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
|
||||
if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
|
||||
if !expr.span.from_expansion() {
|
||||
let msg = "this comparison involving the minimum or maximum element for this \
|
||||
type contains a case that is always true or always false";
|
||||
|
||||
let conclusion = match result {
|
||||
AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
|
||||
AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
|
||||
AbsurdComparisonResult::InequalityImpossible => format!(
|
||||
"the case where the two sides are not equal never occurs, consider using `{} == {}` \
|
||||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs")
|
||||
),
|
||||
};
|
||||
|
||||
let help = format!(
|
||||
"because `{}` is the {} value for this type, {}",
|
||||
snippet(cx, culprit.expr.span, "x"),
|
||||
match culprit.which {
|
||||
ExtremeType::Minimum => "minimum",
|
||||
ExtremeType::Maximum => "maximum",
|
||||
},
|
||||
conclusion
|
||||
);
|
||||
|
||||
span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ExtremeType {
|
||||
Minimum,
|
||||
Maximum,
|
||||
}
|
||||
|
||||
struct ExtremeExpr<'a> {
|
||||
which: ExtremeType,
|
||||
expr: &'a Expr<'a>,
|
||||
}
|
||||
|
||||
enum AbsurdComparisonResult {
|
||||
AlwaysFalse,
|
||||
AlwaysTrue,
|
||||
InequalityImpossible,
|
||||
}
|
||||
|
||||
fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
|
||||
let precast_ty = cx.typeck_results().expr_ty(cast_exp);
|
||||
let cast_ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn detect_absurd_comparison<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
op: BinOpKind,
|
||||
lhs: &'tcx Expr<'_>,
|
||||
rhs: &'tcx Expr<'_>,
|
||||
) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
|
||||
use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
|
||||
use ExtremeType::{Maximum, Minimum};
|
||||
// absurd comparison only makes sense on primitive types
|
||||
// primitive types don't implement comparison operators with each other
|
||||
if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// comparisons between fix sized types and target sized types are considered unanalyzable
|
||||
if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
|
||||
|
||||
let lx = detect_extreme_expr(cx, normalized_lhs);
|
||||
let rx = detect_extreme_expr(cx, normalized_rhs);
|
||||
|
||||
Some(match rel {
|
||||
Rel::Lt => {
|
||||
match (lx, rx) {
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
|
||||
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
match (lx, rx) {
|
||||
(Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
|
||||
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
|
||||
(_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
Rel::Ne | Rel::Eq => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
let cv = constant(cx, cx.typeck_results(), expr)?.0;
|
||||
|
||||
let which = match (ty.kind(), cv) {
|
||||
(&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
|
||||
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
|
||||
ExtremeType::Minimum
|
||||
},
|
||||
|
||||
(&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
|
||||
(&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
|
||||
ExtremeType::Maximum
|
||||
},
|
||||
(&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
Some(ExtremeExpr { which, expr })
|
||||
}
|
377
clippy_lints/src/implicit_hasher.rs
Normal file
377
clippy_lints/src/implicit_hasher.rs
Normal file
@ -0,0 +1,377 @@
|
||||
#![allow(rustc::default_hash_types)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_errors::DiagnosticBuilder;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{Ty, TyS, TypeckResults};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{differing_macro_contexts, match_path};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for public `impl` or `fn` missing generalization
|
||||
/// over different hashers and implicitly defaulting to the default hashing
|
||||
/// algorithm (`SipHash`).
|
||||
///
|
||||
/// **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be
|
||||
/// used with them.
|
||||
///
|
||||
/// **Known problems:** Suggestions for replacing constructors can contain
|
||||
/// false-positives. Also applying suggestions can require modification of other
|
||||
/// pieces of code, possibly including external crates.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # use std::hash::{Hash, BuildHasher};
|
||||
/// # trait Serialize {};
|
||||
/// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
|
||||
///
|
||||
/// pub fn foo(map: &mut HashMap<i32, i32>) { }
|
||||
/// ```
|
||||
/// could be rewritten as
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # use std::hash::{Hash, BuildHasher};
|
||||
/// # trait Serialize {};
|
||||
/// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
|
||||
///
|
||||
/// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
|
||||
/// ```
|
||||
pub IMPLICIT_HASHER,
|
||||
pedantic,
|
||||
"missing generalization over different hashers"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
||||
#[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
use rustc_span::BytePos;
|
||||
|
||||
fn suggestion<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
diag: &mut DiagnosticBuilder<'_>,
|
||||
generics_span: Span,
|
||||
generics_suggestion_span: Span,
|
||||
target: &ImplicitHasherType<'_>,
|
||||
vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
|
||||
) {
|
||||
let generics_snip = snippet(cx, generics_span, "");
|
||||
// trim `<` `>`
|
||||
let generics_snip = if generics_snip.is_empty() {
|
||||
""
|
||||
} else {
|
||||
&generics_snip[1..generics_snip.len() - 1]
|
||||
};
|
||||
|
||||
multispan_sugg(
|
||||
diag,
|
||||
"consider adding a type parameter",
|
||||
vec![
|
||||
(
|
||||
generics_suggestion_span,
|
||||
format!(
|
||||
"<{}{}S: ::std::hash::BuildHasher{}>",
|
||||
generics_snip,
|
||||
if generics_snip.is_empty() { "" } else { ", " },
|
||||
if vis.suggestions.is_empty() {
|
||||
""
|
||||
} else {
|
||||
// request users to add `Default` bound so that generic constructors can be used
|
||||
" + Default"
|
||||
},
|
||||
),
|
||||
),
|
||||
(
|
||||
target.span(),
|
||||
format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if !vis.suggestions.is_empty() {
|
||||
multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
|
||||
}
|
||||
}
|
||||
|
||||
if !cx.access_levels.is_exported(item.hir_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Impl(ref impl_) => {
|
||||
let mut vis = ImplicitHasherTypeVisitor::new(cx);
|
||||
vis.visit_ty(impl_.self_ty);
|
||||
|
||||
for target in &vis.found {
|
||||
if differing_macro_contexts(item.span, target.span()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let generics_suggestion_span = impl_.generics.span.substitute_dummy({
|
||||
let pos = snippet_opt(cx, item.span.until(target.span()))
|
||||
.and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
|
||||
if let Some(pos) = pos {
|
||||
Span::new(pos, pos, item.span.data().ctxt)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
|
||||
for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
|
||||
ctr_vis.visit_impl_item(item);
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPLICIT_HASHER,
|
||||
target.span(),
|
||||
&format!(
|
||||
"impl for `{}` should be generalized over different hashers",
|
||||
target.type_name()
|
||||
),
|
||||
move |diag| {
|
||||
suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
ItemKind::Fn(ref sig, ref generics, body_id) => {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
for ty in sig.decl.inputs {
|
||||
let mut vis = ImplicitHasherTypeVisitor::new(cx);
|
||||
vis.visit_ty(ty);
|
||||
|
||||
for target in &vis.found {
|
||||
if in_external_macro(cx.sess(), generics.span) {
|
||||
continue;
|
||||
}
|
||||
let generics_suggestion_span = generics.span.substitute_dummy({
|
||||
let pos = snippet_opt(cx, item.span.until(body.params[0].pat.span))
|
||||
.and_then(|snip| {
|
||||
let i = snip.find("fn")?;
|
||||
Some(item.span.lo() + BytePos((i + (&snip[i..]).find('(')?) as u32))
|
||||
})
|
||||
.expect("failed to create span for type parameters");
|
||||
Span::new(pos, pos, item.span.data().ctxt)
|
||||
});
|
||||
|
||||
let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
|
||||
ctr_vis.visit_body(body);
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IMPLICIT_HASHER,
|
||||
target.span(),
|
||||
&format!(
|
||||
"parameter of type `{}` should be generalized over different hashers",
|
||||
target.type_name()
|
||||
),
|
||||
move |diag| {
|
||||
suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ImplicitHasherType<'tcx> {
|
||||
HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
|
||||
HashSet(Span, Ty<'tcx>, Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl<'tcx> ImplicitHasherType<'tcx> {
|
||||
/// Checks that `ty` is a target type without a `BuildHasher`.
|
||||
fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
|
||||
if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind {
|
||||
let params: Vec<_> = path
|
||||
.segments
|
||||
.last()
|
||||
.as_ref()?
|
||||
.args
|
||||
.as_ref()?
|
||||
.args
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
GenericArg::Type(ty) => Some(ty),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let params_len = params.len();
|
||||
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::hashmap_type) && params_len == 2 {
|
||||
Some(ImplicitHasherType::HashMap(
|
||||
hir_ty.span,
|
||||
ty,
|
||||
snippet(cx, params[0].span, "K"),
|
||||
snippet(cx, params[1].span, "V"),
|
||||
))
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::hashset_type) && params_len == 1 {
|
||||
Some(ImplicitHasherType::HashSet(
|
||||
hir_ty.span,
|
||||
ty,
|
||||
snippet(cx, params[0].span, "T"),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
ImplicitHasherType::HashMap(..) => "HashMap",
|
||||
ImplicitHasherType::HashSet(..) => "HashSet",
|
||||
}
|
||||
}
|
||||
|
||||
fn type_arguments(&self) -> String {
|
||||
match *self {
|
||||
ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{}, {}", k, v),
|
||||
ImplicitHasherType::HashSet(.., ref t) => format!("{}", t),
|
||||
}
|
||||
}
|
||||
|
||||
fn ty(&self) -> Ty<'tcx> {
|
||||
match *self {
|
||||
ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
match *self {
|
||||
ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ImplicitHasherTypeVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
found: Vec<ImplicitHasherType<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>) -> Self {
|
||||
Self { cx, found: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
|
||||
if let Some(target) = ImplicitHasherType::new(self.cx, t) {
|
||||
self.found.push(target);
|
||||
}
|
||||
|
||||
walk_ty(self, t);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks for default-hasher-dependent constructors like `HashMap::new`.
|
||||
struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
|
||||
target: &'b ImplicitHasherType<'tcx>,
|
||||
suggestions: BTreeMap<Span, String>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
maybe_typeck_results: cx.maybe_typeck_results(),
|
||||
target,
|
||||
suggestions: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_body(&mut self, body: &'tcx Body<'_>) {
|
||||
let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id()));
|
||||
walk_body(self, body);
|
||||
self.maybe_typeck_results = old_maybe_typeck_results;
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref fun, ref args) = e.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind;
|
||||
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
|
||||
then {
|
||||
if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if match_path(ty_path, &paths::HASHMAP) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashMap::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
self.suggestions.insert(
|
||||
e.span,
|
||||
format!(
|
||||
"HashMap::with_capacity_and_hasher({}, Default::default())",
|
||||
snippet(self.cx, args[0].span, "capacity"),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if match_path(ty_path, &paths::HASHSET) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashSet::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
self.suggestions.insert(
|
||||
e.span,
|
||||
format!(
|
||||
"HashSet::with_capacity_and_hasher({}, Default::default())",
|
||||
snippet(self.cx, args[0].span, "capacity"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk_expr(self, e);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
}
|
221
clippy_lints/src/invalid_upcast_comparisons.rs
Normal file
221
clippy_lints/src/invalid_upcast_comparisons.rs
Normal file
@ -0,0 +1,221 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, IntTy, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::LayoutOf;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
|
||||
use clippy_utils::comparisons::Rel;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{comparisons, sext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for comparisons where the relation is always either
|
||||
/// true or false, but where one side has been upcast so that the comparison is
|
||||
/// necessary. Only integer types are checked.
|
||||
///
|
||||
/// **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`
|
||||
/// will mistakenly imply that it is possible for `x` to be outside the range of
|
||||
/// `u8`.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// https://github.com/rust-lang/rust-clippy/issues/886
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: u8 = 1;
|
||||
/// (x as u32) > 300;
|
||||
/// ```
|
||||
pub INVALID_UPCAST_COMPARISONS,
|
||||
pedantic,
|
||||
"a comparison involving an upcast which is always true or false"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq)]
|
||||
enum FullInt {
|
||||
S(i128),
|
||||
U(u128),
|
||||
}
|
||||
|
||||
impl FullInt {
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
#[must_use]
|
||||
fn cmp_s_u(s: i128, u: u128) -> Ordering {
|
||||
if s < 0 {
|
||||
Ordering::Less
|
||||
} else if u > (i128::MAX as u128) {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
(s as u128).cmp(&u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FullInt {
|
||||
#[must_use]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.partial_cmp(other).expect("`partial_cmp` only returns `Some(_)`") == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for FullInt {
|
||||
#[must_use]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(match (self, other) {
|
||||
(&Self::S(s), &Self::S(o)) => s.cmp(&o),
|
||||
(&Self::U(s), &Self::U(o)) => s.cmp(&o),
|
||||
(&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
|
||||
(&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for FullInt {
|
||||
#[must_use]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(other)
|
||||
.expect("`partial_cmp` for FullInt can never return `None`")
|
||||
}
|
||||
}
|
||||
|
||||
fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
|
||||
if let ExprKind::Cast(ref cast_exp, _) = expr.kind {
|
||||
let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
|
||||
let cast_ty = cx.typeck_results().expr_ty(expr);
|
||||
// if it's a cast from i32 to u32 wrapping will invalidate all these checks
|
||||
if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
|
||||
return None;
|
||||
}
|
||||
match pre_cast_ty.kind() {
|
||||
ty::Int(int_ty) => Some(match int_ty {
|
||||
IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
|
||||
IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
|
||||
IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
|
||||
IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
|
||||
IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
|
||||
IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
|
||||
}),
|
||||
ty::Uint(uint_ty) => Some(match uint_ty {
|
||||
UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
|
||||
UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
|
||||
UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
|
||||
UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
|
||||
UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
|
||||
UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<FullInt> {
|
||||
let val = constant(cx, cx.typeck_results(), expr)?.0;
|
||||
if let Constant::Int(const_int) = val {
|
||||
match *cx.typeck_results().expr_ty(expr).kind() {
|
||||
ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
|
||||
ty::Uint(_) => Some(FullInt::U(const_int)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
|
||||
if let ExprKind::Cast(ref cast_val, _) = expr.kind {
|
||||
span_lint(
|
||||
cx,
|
||||
INVALID_UPCAST_COMPARISONS,
|
||||
span,
|
||||
&format!(
|
||||
"because of the numeric bounds on `{}` prior to casting, this expression is always {}",
|
||||
snippet(cx, cast_val.span, "the expression"),
|
||||
if always { "true" } else { "false" },
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn upcast_comparison_bounds_err<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
rel: comparisons::Rel,
|
||||
lhs_bounds: Option<(FullInt, FullInt)>,
|
||||
lhs: &'tcx Expr<'_>,
|
||||
rhs: &'tcx Expr<'_>,
|
||||
invert: bool,
|
||||
) {
|
||||
if let Some((lb, ub)) = lhs_bounds {
|
||||
if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
|
||||
if rel == Rel::Eq || rel == Rel::Ne {
|
||||
if norm_rhs_val < lb || norm_rhs_val > ub {
|
||||
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
|
||||
}
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val < lb
|
||||
} else {
|
||||
ub < norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val <= lb
|
||||
} else {
|
||||
ub <= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, true)
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val >= ub
|
||||
} else {
|
||||
lb >= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val > ub
|
||||
} else {
|
||||
lb > norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind {
|
||||
let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
|
||||
let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
|
||||
val
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
|
||||
let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
|
||||
|
||||
upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
|
||||
upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -164,6 +164,7 @@ mod consts;
|
||||
mod utils;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod absurd_extreme_comparisons;
|
||||
mod approx_const;
|
||||
mod arithmetic;
|
||||
mod as_conversions;
|
||||
@ -231,6 +232,7 @@ mod if_let_mutex;
|
||||
mod if_let_some_result;
|
||||
mod if_not_else;
|
||||
mod if_then_some_else_none;
|
||||
mod implicit_hasher;
|
||||
mod implicit_return;
|
||||
mod implicit_saturating_sub;
|
||||
mod inconsistent_struct_constructor;
|
||||
@ -241,6 +243,7 @@ mod inherent_to_string;
|
||||
mod inline_fn_without_body;
|
||||
mod int_plus_one;
|
||||
mod integer_division;
|
||||
mod invalid_upcast_comparisons;
|
||||
mod items_after_statements;
|
||||
mod large_const_arrays;
|
||||
mod large_enum_variant;
|
||||
@ -559,6 +562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
|
||||
&approx_const::APPROX_CONSTANT,
|
||||
&arithmetic::FLOAT_ARITHMETIC,
|
||||
&arithmetic::INTEGER_ARITHMETIC,
|
||||
@ -683,6 +687,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&if_let_some_result::IF_LET_SOME_RESULT,
|
||||
&if_not_else::IF_NOT_ELSE,
|
||||
&if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
&implicit_hasher::IMPLICIT_HASHER,
|
||||
&implicit_return::IMPLICIT_RETURN,
|
||||
&implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
|
||||
&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
|
||||
@ -696,6 +701,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
|
||||
&int_plus_one::INT_PLUS_ONE,
|
||||
&integer_division::INTEGER_DIVISION,
|
||||
&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
|
||||
&items_after_statements::ITEMS_AFTER_STATEMENTS,
|
||||
&large_const_arrays::LARGE_CONST_ARRAYS,
|
||||
&large_enum_variant::LARGE_ENUM_VARIANT,
|
||||
@ -957,11 +963,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
&transmute::WRONG_TRANSMUTE,
|
||||
&transmuting_null::TRANSMUTING_NULL,
|
||||
&try_err::TRY_ERR,
|
||||
&types::ABSURD_EXTREME_COMPARISONS,
|
||||
&types::BORROWED_BOX,
|
||||
&types::BOX_VEC,
|
||||
&types::IMPLICIT_HASHER,
|
||||
&types::INVALID_UPCAST_COMPARISONS,
|
||||
&types::LINKEDLIST,
|
||||
&types::OPTION_OPTION,
|
||||
&types::RC_BUFFER,
|
||||
@ -1030,7 +1033,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
|
||||
store.register_late_pass(|| box serde_api::SerdeApi);
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold));
|
||||
store.register_late_pass(|| box booleans::NonminimalBool);
|
||||
store.register_late_pass(|| box eq_op::EqOp);
|
||||
store.register_late_pass(|| box enum_clike::UnportableVariant);
|
||||
@ -1093,8 +1097,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box main_recursion::MainRecursion::default());
|
||||
store.register_late_pass(|| box lifetimes::Lifetimes);
|
||||
store.register_late_pass(|| box entry::HashMapPass);
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
|
||||
store.register_late_pass(|| box minmax::MinMaxPass);
|
||||
store.register_late_pass(|| box open_options::OpenOptions);
|
||||
store.register_late_pass(|| box zero_div_zero::ZeroDiv);
|
||||
@ -1116,8 +1118,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
|
||||
store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
|
||||
store.register_late_pass(|| box empty_enum::EmptyEnum);
|
||||
store.register_late_pass(|| box types::AbsurdExtremeComparisons);
|
||||
store.register_late_pass(|| box types::InvalidUpcastComparisons);
|
||||
store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons);
|
||||
store.register_late_pass(|| box invalid_upcast_comparisons::InvalidUpcastComparisons);
|
||||
store.register_late_pass(|| box regex::Regex::default());
|
||||
store.register_late_pass(|| box copies::CopyAndPaste);
|
||||
store.register_late_pass(|| box copy_iterator::CopyIterator);
|
||||
@ -1161,7 +1163,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| box infinite_iter::InfiniteIter);
|
||||
store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody);
|
||||
store.register_late_pass(|| box useless_conversion::UselessConversion::default());
|
||||
store.register_late_pass(|| box types::ImplicitHasher);
|
||||
store.register_late_pass(|| box implicit_hasher::ImplicitHasher);
|
||||
store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
|
||||
store.register_late_pass(|| box double_comparison::DoubleComparisons);
|
||||
store.register_late_pass(|| box question_mark::QuestionMark);
|
||||
@ -1374,8 +1376,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&functions::MUST_USE_CANDIDATE),
|
||||
LintId::of(&functions::TOO_MANY_LINES),
|
||||
LintId::of(&if_not_else::IF_NOT_ELSE),
|
||||
LintId::of(&implicit_hasher::IMPLICIT_HASHER),
|
||||
LintId::of(&implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
|
||||
LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
|
||||
LintId::of(&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
|
||||
LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
|
||||
LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
|
||||
LintId::of(&let_underscore::LET_UNDERSCORE_DROP),
|
||||
@ -1414,8 +1418,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(&types::IMPLICIT_HASHER),
|
||||
LintId::of(&types::INVALID_UPCAST_COMPARISONS),
|
||||
LintId::of(&types::LINKEDLIST),
|
||||
LintId::of(&types::OPTION_OPTION),
|
||||
LintId::of(&unicode::NON_ASCII_LITERAL),
|
||||
@ -1445,6 +1447,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::all", Some("clippy"), vec![
|
||||
LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&approx_const::APPROX_CONSTANT),
|
||||
LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
|
||||
@ -1705,7 +1708,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(&transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(&try_err::TRY_ERR),
|
||||
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&types::BORROWED_BOX),
|
||||
LintId::of(&types::BOX_VEC),
|
||||
LintId::of(&types::REDUNDANT_ALLOCATION),
|
||||
@ -1945,6 +1947,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
]);
|
||||
|
||||
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
|
||||
LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&approx_const::APPROX_CONSTANT),
|
||||
LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC),
|
||||
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
|
||||
@ -2007,7 +2010,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(&transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(&transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
|
||||
LintId::of(&unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
|
File diff suppressed because it is too large
Load Diff
79
clippy_lints/src/types/type_complexity.rs
Normal file
79
clippy_lints/src/types/type_complexity.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_ty, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{GenericParamKind, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::TYPE_COMPLEXITY;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool {
|
||||
let score = {
|
||||
let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
|
||||
visitor.visit_ty(ty);
|
||||
visitor.score
|
||||
};
|
||||
|
||||
if score > type_complexity_threshold {
|
||||
span_lint(
|
||||
cx,
|
||||
TYPE_COMPLEXITY,
|
||||
ty.span,
|
||||
"very complex type used. Consider factoring parts into `type` definitions",
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks a type and assigns a complexity score to it.
|
||||
struct TypeComplexityVisitor {
|
||||
/// total complexity score of the type
|
||||
score: u64,
|
||||
/// current nesting level
|
||||
nest: u64,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
|
||||
let (add_score, sub_nest) = match ty.kind {
|
||||
// _, &x and *x have only small overhead; don't mess with nesting level
|
||||
TyKind::Infer | TyKind::Ptr(..) | TyKind::Rptr(..) => (1, 0),
|
||||
|
||||
// the "normal" components of a type: named types, arrays/tuples
|
||||
TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
|
||||
|
||||
// function types bring a lot of overhead
|
||||
TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
|
||||
|
||||
TyKind::TraitObject(ref param_bounds, _, _) => {
|
||||
let has_lifetime_parameters = param_bounds.iter().any(|bound| {
|
||||
bound
|
||||
.bound_generic_params
|
||||
.iter()
|
||||
.any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
|
||||
});
|
||||
if has_lifetime_parameters {
|
||||
// complex trait bounds like A<'a, 'b>
|
||||
(50 * self.nest, 1)
|
||||
} else {
|
||||
// simple trait bounds like A + B
|
||||
(20 * self.nest, 0)
|
||||
}
|
||||
},
|
||||
|
||||
_ => (0, 0),
|
||||
};
|
||||
self.score += add_score;
|
||||
self.nest += sub_nest;
|
||||
walk_ty(self, ty);
|
||||
self.nest -= sub_nest;
|
||||
}
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
@ -5,6 +5,9 @@
|
||||
extern crate alloc;
|
||||
use alloc::collections::linked_list::LinkedList;
|
||||
|
||||
const C: LinkedList<i32> = LinkedList::new();
|
||||
static S: LinkedList<i32> = LinkedList::new();
|
||||
|
||||
trait Foo {
|
||||
type Baz = LinkedList<u8>;
|
||||
fn foo(_: LinkedList<u8>);
|
@ -1,14 +1,30 @@
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:9:16
|
||||
--> $DIR/linkedlist.rs:8:10
|
||||
|
|
||||
LL | type Baz = LinkedList<u8>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
LL | const C: LinkedList<i32> = LinkedList::new();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::linkedlist` implied by `-D warnings`
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:10:15
|
||||
--> $DIR/linkedlist.rs:9:11
|
||||
|
|
||||
LL | static S: LinkedList<i32> = LinkedList::new();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/linkedlist.rs:12:16
|
||||
|
|
||||
LL | type Baz = LinkedList<u8>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/linkedlist.rs:13:15
|
||||
|
|
||||
LL | fn foo(_: LinkedList<u8>);
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -16,7 +32,7 @@ LL | fn foo(_: LinkedList<u8>);
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:11:23
|
||||
--> $DIR/linkedlist.rs:14:23
|
||||
|
|
||||
LL | const BAR: Option<LinkedList<u8>>;
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -24,7 +40,7 @@ LL | const BAR: Option<LinkedList<u8>>;
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:22:15
|
||||
--> $DIR/linkedlist.rs:25:15
|
||||
|
|
||||
LL | fn foo(_: LinkedList<u8>) {}
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -32,7 +48,7 @@ LL | fn foo(_: LinkedList<u8>) {}
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:25:39
|
||||
--> $DIR/linkedlist.rs:28:39
|
||||
|
|
||||
LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
|
||||
| ^^^^^^^^^^^^^^
|
||||
@ -40,12 +56,12 @@ LL | pub fn test(my_favourite_linked_list: LinkedList<u8>) {
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure?
|
||||
--> $DIR/dlist.rs:29:29
|
||||
--> $DIR/linkedlist.rs:32:29
|
||||
|
|
||||
LL | pub fn test_ret() -> Option<LinkedList<u8>> {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: a `VecDeque` might work
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 8 previous errors
|
||||
|
@ -1,6 +1,9 @@
|
||||
#![deny(clippy::option_option)]
|
||||
#![allow(clippy::unnecessary_wraps)]
|
||||
|
||||
const C: Option<Option<i32>> = None;
|
||||
static S: Option<Option<i32>> = None;
|
||||
|
||||
fn input(_: Option<Option<u8>>) {}
|
||||
|
||||
fn output() -> Option<Option<u8>> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:4:13
|
||||
--> $DIR/option_option.rs:4:10
|
||||
|
|
||||
LL | fn input(_: Option<Option<u8>>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
LL | const C: Option<Option<i32>> = None;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/option_option.rs:1:9
|
||||
@ -11,58 +11,70 @@ LL | #![deny(clippy::option_option)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:6:16
|
||||
--> $DIR/option_option.rs:5:11
|
||||
|
|
||||
LL | static S: Option<Option<i32>> = None;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:7:13
|
||||
|
|
||||
LL | fn input(_: Option<Option<u8>>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:9:16
|
||||
|
|
||||
LL | fn output() -> Option<Option<u8>> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:10:27
|
||||
--> $DIR/option_option.rs:13:27
|
||||
|
|
||||
LL | fn output_nested() -> Vec<Option<Option<u8>>> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:15:30
|
||||
--> $DIR/option_option.rs:18:30
|
||||
|
|
||||
LL | fn output_nested_nested() -> Option<Option<Option<u8>>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:20:8
|
||||
--> $DIR/option_option.rs:23:8
|
||||
|
|
||||
LL | x: Option<Option<u8>>,
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:24:23
|
||||
--> $DIR/option_option.rs:27:23
|
||||
|
|
||||
LL | fn struct_fn() -> Option<Option<u8>> {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:30:22
|
||||
--> $DIR/option_option.rs:33:22
|
||||
|
|
||||
LL | fn trait_fn() -> Option<Option<u8>>;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:34:11
|
||||
--> $DIR/option_option.rs:37:11
|
||||
|
|
||||
LL | Tuple(Option<Option<u8>>),
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:35:17
|
||||
--> $DIR/option_option.rs:38:17
|
||||
|
|
||||
LL | Struct { x: Option<Option<u8>> },
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
|
||||
--> $DIR/option_option.rs:76:14
|
||||
--> $DIR/option_option.rs:79:14
|
||||
|
|
||||
LL | foo: Option<Option<Cow<'a, str>>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:7:12
|
||||
--> $DIR/type_complexity.rs:7:12
|
||||
|
|
||||
LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -7,85 +7,85 @@ LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
|
||||
= note: `-D clippy::type-complexity` implied by `-D warnings`
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:8:12
|
||||
--> $DIR/type_complexity.rs:8:12
|
||||
|
|
||||
LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:11:8
|
||||
--> $DIR/type_complexity.rs:11:8
|
||||
|
|
||||
LL | f: Vec<Vec<Box<(u32, u32, u32, u32)>>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:14:11
|
||||
--> $DIR/type_complexity.rs:14:11
|
||||
|
|
||||
LL | struct Ts(Vec<Vec<Box<(u32, u32, u32, u32)>>>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:17:11
|
||||
--> $DIR/type_complexity.rs:17:11
|
||||
|
|
||||
LL | Tuple(Vec<Vec<Box<(u32, u32, u32, u32)>>>),
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:18:17
|
||||
--> $DIR/type_complexity.rs:18:17
|
||||
|
|
||||
LL | Struct { f: Vec<Vec<Box<(u32, u32, u32, u32)>>> },
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:22:14
|
||||
--> $DIR/type_complexity.rs:22:14
|
||||
|
|
||||
LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0))));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:23:30
|
||||
--> $DIR/type_complexity.rs:23:30
|
||||
|
|
||||
LL | fn impl_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:27:14
|
||||
--> $DIR/type_complexity.rs:27:14
|
||||
|
|
||||
LL | const A: Vec<Vec<Box<(u32, u32, u32, u32)>>>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:28:14
|
||||
--> $DIR/type_complexity.rs:28:14
|
||||
|
|
||||
LL | type B = Vec<Vec<Box<(u32, u32, u32, u32)>>>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:29:25
|
||||
--> $DIR/type_complexity.rs:29:25
|
||||
|
|
||||
LL | fn method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:30:29
|
||||
--> $DIR/type_complexity.rs:30:29
|
||||
|
|
||||
LL | fn def_method(&self, p: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:33:15
|
||||
--> $DIR/type_complexity.rs:33:15
|
||||
|
|
||||
LL | fn test1() -> Vec<Vec<Box<(u32, u32, u32, u32)>>> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:37:14
|
||||
--> $DIR/type_complexity.rs:37:14
|
||||
|
|
||||
LL | fn test2(_x: Vec<Vec<Box<(u32, u32, u32, u32)>>>) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: very complex type used. Consider factoring parts into `type` definitions
|
||||
--> $DIR/complex_types.rs:40:13
|
||||
--> $DIR/type_complexity.rs:40:13
|
||||
|
|
||||
LL | let _y: Vec<Vec<Box<(u32, u32, u32, u32)>>> = vec![];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]);
|
||||
/// The following should trigger the lint
|
||||
mod should_trigger {
|
||||
use super::SizedStruct;
|
||||
const C: Vec<i32> = Vec::new();
|
||||
static S: Vec<i32> = Vec::new();
|
||||
|
||||
struct StructWithVecBox {
|
||||
sized_type: Vec<SizedStruct>,
|
||||
|
@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]);
|
||||
/// The following should trigger the lint
|
||||
mod should_trigger {
|
||||
use super::SizedStruct;
|
||||
const C: Vec<Box<i32>> = Vec::new();
|
||||
static S: Vec<Box<i32>> = Vec::new();
|
||||
|
||||
struct StructWithVecBox {
|
||||
sized_type: Vec<Box<SizedStruct>>,
|
||||
|
@ -1,28 +1,40 @@
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:14:21
|
||||
--> $DIR/vec_box_sized.rs:12:14
|
||||
|
|
||||
LL | sized_type: Vec<Box<SizedStruct>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
|
||||
LL | const C: Vec<Box<i32>> = Vec::new();
|
||||
| ^^^^^^^^^^^^^ help: try: `Vec<i32>`
|
||||
|
|
||||
= note: `-D clippy::vec-box` implied by `-D warnings`
|
||||
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:17:14
|
||||
--> $DIR/vec_box_sized.rs:13:15
|
||||
|
|
||||
LL | static S: Vec<Box<i32>> = Vec::new();
|
||||
| ^^^^^^^^^^^^^ help: try: `Vec<i32>`
|
||||
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:16:21
|
||||
|
|
||||
LL | sized_type: Vec<Box<SizedStruct>>,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
|
||||
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:19:14
|
||||
|
|
||||
LL | struct A(Vec<Box<SizedStruct>>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec<SizedStruct>`
|
||||
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:18:18
|
||||
--> $DIR/vec_box_sized.rs:20:18
|
||||
|
|
||||
LL | struct B(Vec<Vec<Box<(u32)>>>);
|
||||
| ^^^^^^^^^^^^^^^ help: try: `Vec<u32>`
|
||||
|
||||
error: `Vec<T>` is already on the heap, the boxing is unnecessary
|
||||
--> $DIR/vec_box_sized.rs:46:23
|
||||
--> $DIR/vec_box_sized.rs:48:23
|
||||
|
|
||||
LL | pub fn f() -> Vec<Box<S>> {
|
||||
| ^^^^^^^^^^^ help: try: `Vec<S>`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user