mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-24 05:33:27 +00:00
Auto merge of #11135 - smoelius:unwrap_or_else_default-fp, r=Centri3
Fix `unwrap_or_else_default` false positive This PR fixes a false positive in the handling of `unwrap_or_else` with a default value when the value is needed for type inference. An easy example to exhibit the false positive is the following: ```rust let option = None; option.unwrap_or_else(Vec::new).push(1); ``` The following code would not compile, because the fact that the value is a `Vec` has been lost: ```rust let option = None; option.unwrap_or_default().push(1); ``` The fix is to: - implement a heuristic to tell whether an expression's type can be determined purely from its subexpressions, and the arguments and locals they use; - apply the heuristic to `unwrap_or_else`'s receiver. The heuristic returns false when applied to `option` in the above example, but it returns true when applied to `option` in either of the following examples: ```rust let option: Option<Vec<u64>> = None; option.unwrap_or_else(Vec::new).push(1); ``` ```rust let option = None::<Vec<u64>>; option.unwrap_or_else(Vec::new).push(1); ``` (Aside: https://github.com/rust-lang/rust-clippy/pull/10120 unfairly contained multiple changes in one PR. I am trying to break that PR up into smaller pieces.) --- changelog: FP: [`unwrap_or_else_default`]: No longer lints if the default value is needed for type inference
This commit is contained in:
commit
7a34143fa3
7 changed files with 568 additions and 2 deletions
|
@ -4,7 +4,7 @@ use super::UNWRAP_OR_ELSE_DEFAULT;
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_default_equivalent_call;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::ty::{expr_type_is_certain, is_type_diagnostic_item};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -17,6 +17,10 @@ pub(super) fn check<'tcx>(
|
|||
recv: &'tcx hir::Expr<'_>,
|
||||
u_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if !expr_type_is_certain(cx, recv) {
|
||||
return;
|
||||
}
|
||||
|
||||
// something.unwrap_or_else(Default::default)
|
||||
// ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr
|
||||
|
|
|
@ -28,6 +28,9 @@ use std::iter;
|
|||
|
||||
use crate::{match_def_path, path_res, paths};
|
||||
|
||||
mod type_certainty;
|
||||
pub use type_certainty::expr_type_is_certain;
|
||||
|
||||
/// Checks if the given type implements copy.
|
||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
|
||||
|
|
122
clippy_utils/src/ty/type_certainty/certainty.rs
Normal file
122
clippy_utils/src/ty/type_certainty/certainty.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use rustc_hir::def_id::DefId;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Certainty {
|
||||
/// Determining the type requires contextual information.
|
||||
Uncertain,
|
||||
|
||||
/// The type can be determined purely from subexpressions. If the argument is `Some(..)`, the
|
||||
/// specific `DefId` is known. Such arguments are needed to handle path segments whose `res` is
|
||||
/// `Res::Err`.
|
||||
Certain(Option<DefId>),
|
||||
|
||||
/// The heuristic believes that more than one `DefId` applies to a type---this is a bug.
|
||||
Contradiction,
|
||||
}
|
||||
|
||||
pub trait Meet {
|
||||
fn meet(self, other: Self) -> Self;
|
||||
}
|
||||
|
||||
pub trait TryJoin: Sized {
|
||||
fn try_join(self, other: Self) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl Meet for Option<DefId> {
|
||||
fn meet(self, other: Self) -> Self {
|
||||
match (self, other) {
|
||||
(None, _) | (_, None) => None,
|
||||
(Some(lhs), Some(rhs)) => (lhs == rhs).then_some(lhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryJoin for Option<DefId> {
|
||||
fn try_join(self, other: Self) -> Option<Self> {
|
||||
match (self, other) {
|
||||
(Some(lhs), Some(rhs)) => (lhs == rhs).then_some(Some(lhs)),
|
||||
(Some(def_id), _) | (_, Some(def_id)) => Some(Some(def_id)),
|
||||
(None, None) => Some(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Meet for Certainty {
|
||||
fn meet(self, other: Self) -> Self {
|
||||
match (self, other) {
|
||||
(Certainty::Uncertain, _) | (_, Certainty::Uncertain) => Certainty::Uncertain,
|
||||
(Certainty::Certain(lhs), Certainty::Certain(rhs)) => Certainty::Certain(lhs.meet(rhs)),
|
||||
(Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner),
|
||||
(Certainty::Contradiction, Certainty::Contradiction) => Certainty::Contradiction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Certainty {
|
||||
/// Join two `Certainty`s preserving their `DefId`s (if any). Generally speaking, this method
|
||||
/// should be used only when `self` and `other` refer directly to types. Otherwise,
|
||||
/// `join_clearing_def_ids` should be used.
|
||||
pub fn join(self, other: Self) -> Self {
|
||||
match (self, other) {
|
||||
(Certainty::Contradiction, _) | (_, Certainty::Contradiction) => Certainty::Contradiction,
|
||||
|
||||
(Certainty::Certain(lhs), Certainty::Certain(rhs)) => {
|
||||
if let Some(inner) = lhs.try_join(rhs) {
|
||||
Certainty::Certain(inner)
|
||||
} else {
|
||||
debug_assert!(false, "Contradiction with {lhs:?} and {rhs:?}");
|
||||
Certainty::Contradiction
|
||||
}
|
||||
},
|
||||
|
||||
(Certainty::Certain(inner), _) | (_, Certainty::Certain(inner)) => Certainty::Certain(inner),
|
||||
|
||||
(Certainty::Uncertain, Certainty::Uncertain) => Certainty::Uncertain,
|
||||
}
|
||||
}
|
||||
|
||||
/// Join two `Certainty`s after clearing their `DefId`s. This method should be used when `self`
|
||||
/// or `other` do not necessarily refer to types, e.g., when they are aggregations of other
|
||||
/// `Certainty`s.
|
||||
pub fn join_clearing_def_ids(self, other: Self) -> Self {
|
||||
self.clear_def_id().join(other.clear_def_id())
|
||||
}
|
||||
|
||||
pub fn clear_def_id(self) -> Certainty {
|
||||
if matches!(self, Certainty::Certain(_)) {
|
||||
Certainty::Certain(None)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_def_id(self, def_id: DefId) -> Certainty {
|
||||
if matches!(self, Certainty::Certain(_)) {
|
||||
Certainty::Certain(Some(def_id))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_def_id(self) -> Option<DefId> {
|
||||
match self {
|
||||
Certainty::Certain(inner) => inner,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_certain(self) -> bool {
|
||||
matches!(self, Self::Certain(_))
|
||||
}
|
||||
}
|
||||
|
||||
/// Think: `iter.all(/* is certain */)`
|
||||
pub fn meet(iter: impl Iterator<Item = Certainty>) -> Certainty {
|
||||
iter.fold(Certainty::Certain(None), Certainty::meet)
|
||||
}
|
||||
|
||||
/// Think: `iter.any(/* is certain */)`
|
||||
pub fn join(iter: impl Iterator<Item = Certainty>) -> Certainty {
|
||||
iter.fold(Certainty::Uncertain, Certainty::join)
|
||||
}
|
315
clippy_utils/src/ty/type_certainty/mod.rs
Normal file
315
clippy_utils/src/ty/type_certainty/mod.rs
Normal file
|
@ -0,0 +1,315 @@
|
|||
//! A heuristic to tell whether an expression's type can be determined purely from its
|
||||
//! subexpressions, and the arguments and locals they use. Put another way, `expr_type_is_certain`
|
||||
//! tries to tell whether an expression's type can be determined without appeal to the surrounding
|
||||
//! context.
|
||||
//!
|
||||
//! This is, in some sense, a counterpart to `let_unit_value`'s `expr_needs_inferred_result`.
|
||||
//! Intuitively, that function determines whether an expression's type is needed for type inference,
|
||||
//! whereas `expr_type_is_certain` determines whether type inference is needed for an expression's
|
||||
//! type.
|
||||
//!
|
||||
//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
|
||||
//! be considered a bug.
|
||||
|
||||
use crate::def_path_res;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor};
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
mod certainty;
|
||||
use certainty::{join, meet, Certainty, Meet};
|
||||
|
||||
pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
expr_type_certainty(cx, expr).is_certain()
|
||||
}
|
||||
|
||||
fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
|
||||
let certainty = match &expr.kind {
|
||||
ExprKind::Unary(_, expr)
|
||||
| ExprKind::Field(expr, _)
|
||||
| ExprKind::Index(expr, _)
|
||||
| ExprKind::AddrOf(_, _, expr) => expr_type_certainty(cx, expr),
|
||||
|
||||
ExprKind::Array(exprs) => join(exprs.iter().map(|expr| expr_type_certainty(cx, expr))),
|
||||
|
||||
ExprKind::Call(callee, args) => {
|
||||
let lhs = expr_type_certainty(cx, callee);
|
||||
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
|
||||
meet(args.iter().map(|arg| expr_type_certainty(cx, arg)))
|
||||
} else {
|
||||
Certainty::Uncertain
|
||||
};
|
||||
lhs.join_clearing_def_ids(rhs)
|
||||
},
|
||||
|
||||
ExprKind::MethodCall(method, receiver, args, _) => {
|
||||
let mut receiver_type_certainty = expr_type_certainty(cx, receiver);
|
||||
// Even if `receiver_type_certainty` is `Certain(Some(..))`, the `Self` type in the method
|
||||
// identified by `type_dependent_def_id(..)` can differ. This can happen as a result of a `deref`,
|
||||
// for example. So update the `DefId` in `receiver_type_certainty` (if any).
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id))
|
||||
{
|
||||
receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
|
||||
};
|
||||
let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
|
||||
let rhs = if type_is_inferrable_from_arguments(cx, expr) {
|
||||
meet(
|
||||
std::iter::once(receiver_type_certainty).chain(args.iter().map(|arg| expr_type_certainty(cx, arg))),
|
||||
)
|
||||
} else {
|
||||
Certainty::Uncertain
|
||||
};
|
||||
lhs.join(rhs)
|
||||
},
|
||||
|
||||
ExprKind::Tup(exprs) => meet(exprs.iter().map(|expr| expr_type_certainty(cx, expr))),
|
||||
|
||||
ExprKind::Binary(_, lhs, rhs) => expr_type_certainty(cx, lhs).meet(expr_type_certainty(cx, rhs)),
|
||||
|
||||
ExprKind::Lit(_) => Certainty::Certain(None),
|
||||
|
||||
ExprKind::Cast(_, ty) => type_certainty(cx, ty),
|
||||
|
||||
ExprKind::If(_, if_expr, Some(else_expr)) => {
|
||||
expr_type_certainty(cx, if_expr).join(expr_type_certainty(cx, else_expr))
|
||||
},
|
||||
|
||||
ExprKind::Path(qpath) => qpath_certainty(cx, qpath, false),
|
||||
|
||||
ExprKind::Struct(qpath, _, _) => qpath_certainty(cx, qpath, true),
|
||||
|
||||
_ => Certainty::Uncertain,
|
||||
};
|
||||
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
if let Some(def_id) = adt_def_id(expr_ty) {
|
||||
certainty.with_def_id(def_id)
|
||||
} else {
|
||||
certainty
|
||||
}
|
||||
}
|
||||
|
||||
struct CertaintyVisitor<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
certainty: Certainty,
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> CertaintyVisitor<'cx, 'tcx> {
|
||||
fn new(cx: &'cx LateContext<'tcx>) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
certainty: Certainty::Certain(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> Visitor<'cx> for CertaintyVisitor<'cx, 'tcx> {
|
||||
fn visit_qpath(&mut self, qpath: &'cx QPath<'_>, hir_id: HirId, _: Span) {
|
||||
self.certainty = self.certainty.meet(qpath_certainty(self.cx, qpath, true));
|
||||
if self.certainty != Certainty::Uncertain {
|
||||
walk_qpath(self, qpath, hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'cx hir::Ty<'_>) {
|
||||
if matches!(ty.kind, TyKind::Infer) {
|
||||
self.certainty = Certainty::Uncertain;
|
||||
}
|
||||
if self.certainty != Certainty::Uncertain {
|
||||
walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty {
|
||||
// Handle `TyKind::Path` specially so that its `DefId` can be preserved.
|
||||
//
|
||||
// Note that `CertaintyVisitor::new` initializes the visitor's internal certainty to
|
||||
// `Certainty::Certain(None)`. Furthermore, if a `TyKind::Path` is encountered while traversing
|
||||
// `ty`, the result of the call to `qpath_certainty` is combined with the visitor's internal
|
||||
// certainty using `Certainty::meet`. Thus, if the `TyKind::Path` were not treated specially here,
|
||||
// the resulting certainty would be `Certainty::Certain(None)`.
|
||||
if let TyKind::Path(qpath) = &ty.kind {
|
||||
return qpath_certainty(cx, qpath, true);
|
||||
}
|
||||
|
||||
let mut visitor = CertaintyVisitor::new(cx);
|
||||
visitor.visit_ty(ty);
|
||||
visitor.certainty
|
||||
}
|
||||
|
||||
fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certainty {
|
||||
let mut visitor = CertaintyVisitor::new(cx);
|
||||
visitor.visit_generic_args(args);
|
||||
visitor.certainty
|
||||
}
|
||||
|
||||
/// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path
|
||||
/// segments generic arguments are are instantiated.
|
||||
///
|
||||
/// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a
|
||||
/// value. So `DefId`s are retained only when `resolves_to_type` is true.
|
||||
fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bool) -> Certainty {
|
||||
let certainty = match qpath {
|
||||
QPath::Resolved(ty, path) => {
|
||||
let len = path.segments.len();
|
||||
path.segments.iter().enumerate().fold(
|
||||
ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)),
|
||||
|parent_certainty, (i, path_segment)| {
|
||||
path_segment_certainty(cx, parent_certainty, path_segment, i != len - 1 || resolves_to_type)
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
QPath::TypeRelative(ty, path_segment) => {
|
||||
path_segment_certainty(cx, type_certainty(cx, ty), path_segment, resolves_to_type)
|
||||
},
|
||||
|
||||
QPath::LangItem(lang_item, _, _) => {
|
||||
cx.tcx
|
||||
.lang_items()
|
||||
.get(*lang_item)
|
||||
.map_or(Certainty::Uncertain, |def_id| {
|
||||
let generics = cx.tcx.generics_of(def_id);
|
||||
if generics.parent_count == 0 && generics.params.is_empty() {
|
||||
Certainty::Certain(if resolves_to_type { Some(def_id) } else { None })
|
||||
} else {
|
||||
Certainty::Uncertain
|
||||
}
|
||||
})
|
||||
},
|
||||
};
|
||||
debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
|
||||
certainty
|
||||
}
|
||||
|
||||
fn path_segment_certainty(
|
||||
cx: &LateContext<'_>,
|
||||
parent_certainty: Certainty,
|
||||
path_segment: &PathSegment<'_>,
|
||||
resolves_to_type: bool,
|
||||
) -> Certainty {
|
||||
let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) {
|
||||
// A definition's type is certain if it refers to something without generics (e.g., a crate or module, or
|
||||
// an unparameterized type), or the generics are instantiated with arguments that are certain.
|
||||
//
|
||||
// If the parent is uncertain, then the current path segment must account for the parent's generic arguments.
|
||||
// Consider the following examples, where the current path segment is `None`:
|
||||
// - `Option::None` // uncertain; parent (i.e., `Option`) is uncertain
|
||||
// - `Option::<Vec<u64>>::None` // certain; parent (i.e., `Option::<..>`) is certain
|
||||
// - `Option::None::<Vec<u64>>` // certain; parent (i.e., `Option`) is uncertain
|
||||
Res::Def(_, def_id) => {
|
||||
// Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE.
|
||||
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
|
||||
let generics = cx.tcx.generics_of(def_id);
|
||||
let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && generics.params.is_empty()
|
||||
{
|
||||
Certainty::Certain(None)
|
||||
} else {
|
||||
Certainty::Uncertain
|
||||
};
|
||||
let rhs = path_segment
|
||||
.args
|
||||
.map_or(Certainty::Uncertain, |args| generic_args_certainty(cx, args));
|
||||
// See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value.
|
||||
let certainty = lhs.join_clearing_def_ids(rhs);
|
||||
if resolves_to_type {
|
||||
certainty.with_def_id(def_id)
|
||||
} else {
|
||||
certainty
|
||||
}
|
||||
} else {
|
||||
Certainty::Certain(None)
|
||||
}
|
||||
},
|
||||
|
||||
Res::PrimTy(_) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::SelfCtor(_) => {
|
||||
Certainty::Certain(None)
|
||||
},
|
||||
|
||||
// `get_parent` because `hir_id` refers to a `Pat`, and we're interested in the node containing the `Pat`.
|
||||
Res::Local(hir_id) => match cx.tcx.hir().get_parent(hir_id) {
|
||||
// An argument's type is always certain.
|
||||
Node::Param(..) => Certainty::Certain(None),
|
||||
// A local's type is certain if its type annotation is certain or it has an initializer whose
|
||||
// type is certain.
|
||||
Node::Local(local) => {
|
||||
let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty));
|
||||
let rhs = local
|
||||
.init
|
||||
.map_or(Certainty::Uncertain, |init| expr_type_certainty(cx, init));
|
||||
let certainty = lhs.join(rhs);
|
||||
if resolves_to_type {
|
||||
certainty
|
||||
} else {
|
||||
certainty.clear_def_id()
|
||||
}
|
||||
},
|
||||
_ => Certainty::Uncertain,
|
||||
},
|
||||
|
||||
_ => Certainty::Uncertain,
|
||||
};
|
||||
debug_assert!(resolves_to_type || certainty.to_def_id().is_none());
|
||||
certainty
|
||||
}
|
||||
|
||||
/// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`.
|
||||
/// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`.
|
||||
fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> {
|
||||
if path_segment.res == Res::Err && let Some(def_id) = parent_certainty.to_def_id() {
|
||||
let mut def_path = cx.get_def_path(def_id);
|
||||
def_path.push(path_segment.ident.name);
|
||||
let reses = def_path_res(cx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>());
|
||||
if let [res] = reses.as_slice() { Some(*res) } else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let Some(callee_def_id) = (match expr.kind {
|
||||
ExprKind::Call(callee, _) => {
|
||||
let callee_ty = cx.typeck_results().expr_ty(callee);
|
||||
if let ty::FnDef(callee_def_id, _) = callee_ty.kind() {
|
||||
Some(*callee_def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(_, _, _, _) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
|
||||
_ => None,
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let generics = cx.tcx.generics_of(callee_def_id);
|
||||
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
|
||||
|
||||
// Check that all type parameters appear in the functions input types.
|
||||
(0..(generics.parent_count + generics.params.len()) as u32).all(|index| {
|
||||
fn_sig
|
||||
.inputs()
|
||||
.iter()
|
||||
.any(|input_ty| contains_param(*input_ty.skip_binder(), index))
|
||||
})
|
||||
}
|
||||
|
||||
fn self_ty<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId) -> Ty<'tcx> {
|
||||
cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()[0]
|
||||
}
|
||||
|
||||
fn adt_def_id(ty: Ty<'_>) -> Option<DefId> {
|
||||
ty.peel_refs().ty_adt_def().map(AdtDef::did)
|
||||
}
|
||||
|
||||
fn contains_param(ty: Ty<'_>, index: u32) -> bool {
|
||||
ty.walk()
|
||||
.any(|arg| matches!(arg.unpack(), GenericArgKind::Type(ty) if ty.is_param(index)))
|
||||
}
|
|
@ -74,4 +74,41 @@ fn unwrap_or_else_default() {
|
|||
empty_string.unwrap_or_default();
|
||||
}
|
||||
|
||||
fn type_certainty(option: Option<Vec<u64>>) {
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option: std::option::Option<std::vec::Vec<u64>> = None;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option: Option<Vec<u64>> = None;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option = std::option::Option::<std::vec::Vec<u64>>::None;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option = Option::<Vec<u64>>::None;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option = std::option::Option::None::<std::vec::Vec<u64>>;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option = Option::None::<Vec<u64>>;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
let option = None::<Vec<u64>>;
|
||||
option.unwrap_or_default().push(1);
|
||||
|
||||
// should not be changed: type annotation with infer, unconcretized initializer
|
||||
let option: Option<Vec<_>> = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
// should not be changed: no type annotation, unconcretized initializer
|
||||
let option = Option::None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
// should not be changed: no type annotation, unconcretized initializer
|
||||
let option = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -74,4 +74,41 @@ fn unwrap_or_else_default() {
|
|||
empty_string.unwrap_or_else(|| "".to_string());
|
||||
}
|
||||
|
||||
fn type_certainty(option: Option<Vec<u64>>) {
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option: std::option::Option<std::vec::Vec<u64>> = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option: Option<Vec<u64>> = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option = std::option::Option::<std::vec::Vec<u64>>::None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option = Option::<Vec<u64>>::None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option = std::option::Option::None::<std::vec::Vec<u64>>;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option = Option::None::<Vec<u64>>;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
let option = None::<Vec<u64>>;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
// should not be changed: type annotation with infer, unconcretized initializer
|
||||
let option: Option<Vec<_>> = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
// should not be changed: no type annotation, unconcretized initializer
|
||||
let option = Option::None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
|
||||
// should not be changed: no type annotation, unconcretized initializer
|
||||
let option = None;
|
||||
option.unwrap_or_else(Vec::new).push(1);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -36,5 +36,53 @@ error: use of `.unwrap_or_else(..)` to construct default value
|
|||
LL | empty_string.unwrap_or_else(|| "".to_string());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `empty_string.unwrap_or_default()`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:78:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:81:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:84:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:87:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:90:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:93:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:96:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: use of `.unwrap_or_else(..)` to construct default value
|
||||
--> $DIR/unwrap_or_else_default.rs:99:5
|
||||
|
|
||||
LL | option.unwrap_or_else(Vec::new).push(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `option.unwrap_or_default()`
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
|
|
Loading…
Reference in a new issue