Auto merge of #94078 - TaKO8Ki:suggest-float-literal-for-float-divided-by-integer, r=estebank
Suggest a float literal when dividing a floating-point type by `{integer}` closes #93829
This commit is contained in:
commit
8c9640e34c
12 changed files with 543 additions and 18 deletions
|
@ -368,6 +368,11 @@ pub enum ObligationCauseCode<'tcx> {
|
||||||
|
|
||||||
/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
|
/// From `match_impl`. The cause for us having to match an impl, and the DefId we are matching against.
|
||||||
MatchImpl(ObligationCause<'tcx>, DefId),
|
MatchImpl(ObligationCause<'tcx>, DefId),
|
||||||
|
|
||||||
|
BinOp {
|
||||||
|
rhs_span: Option<Span>,
|
||||||
|
is_lit: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The 'location' at which we try to perform HIR-based wf checking.
|
/// The 'location' at which we try to perform HIR-based wf checking.
|
||||||
|
|
|
@ -501,6 +501,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
err.span_label(enclosing_scope_span, s.as_str());
|
err.span_label(enclosing_scope_span, s.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
|
||||||
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
|
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
|
||||||
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
|
self.suggest_fn_call(&obligation, &mut err, trait_predicate);
|
||||||
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
|
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
|
||||||
|
|
|
@ -174,6 +174,13 @@ pub trait InferCtxtExt<'tcx> {
|
||||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn suggest_floating_point_literal(
|
||||||
|
&self,
|
||||||
|
obligation: &PredicateObligation<'tcx>,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
trait_ref: &ty::PolyTraitRef<'tcx>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
|
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
|
||||||
|
@ -1910,8 +1917,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
| ObligationCauseCode::AwaitableExpr(_)
|
| ObligationCauseCode::AwaitableExpr(_)
|
||||||
| ObligationCauseCode::ForLoopIterator
|
| ObligationCauseCode::ForLoopIterator
|
||||||
| ObligationCauseCode::QuestionMark
|
| ObligationCauseCode::QuestionMark
|
||||||
|
| ObligationCauseCode::CheckAssociatedTypeBounds { .. }
|
||||||
| ObligationCauseCode::LetElse
|
| ObligationCauseCode::LetElse
|
||||||
| ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {}
|
| ObligationCauseCode::BinOp { .. } => {}
|
||||||
ObligationCauseCode::SliceOrArrayElem => {
|
ObligationCauseCode::SliceOrArrayElem => {
|
||||||
err.note("slice and array elements must have `Sized` type");
|
err.note("slice and array elements must have `Sized` type");
|
||||||
}
|
}
|
||||||
|
@ -2497,6 +2505,32 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn suggest_floating_point_literal(
|
||||||
|
&self,
|
||||||
|
obligation: &PredicateObligation<'tcx>,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
trait_ref: &ty::PolyTraitRef<'tcx>,
|
||||||
|
) {
|
||||||
|
let rhs_span = match obligation.cause.code() {
|
||||||
|
ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit } if *is_lit => span,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
match (
|
||||||
|
trait_ref.skip_binder().self_ty().kind(),
|
||||||
|
trait_ref.skip_binder().substs.type_at(1).kind(),
|
||||||
|
) {
|
||||||
|
(ty::Float(_), ty::Infer(InferTy::IntVar(_))) => {
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
rhs_span.shrink_to_hi(),
|
||||||
|
"consider using a floating-point literal by writing it with `.0`",
|
||||||
|
String::from(".0"),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collect all the returned expressions within the input expression.
|
/// Collect all the returned expressions within the input expression.
|
||||||
|
|
|
@ -438,6 +438,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
value: T,
|
||||||
|
opt_input_expr: Option<&hir::Expr<'_>>,
|
||||||
|
) -> InferOk<'tcx, T>
|
||||||
|
where
|
||||||
|
T: TypeFoldable<'tcx>,
|
||||||
|
{
|
||||||
|
self.inh.partially_normalize_associated_types_in(
|
||||||
|
ObligationCause::new(
|
||||||
|
span,
|
||||||
|
self.body_id,
|
||||||
|
traits::BinOp {
|
||||||
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
|
is_lit: opt_input_expr
|
||||||
|
.map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
self.param_env,
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn require_type_meets(
|
pub fn require_type_meets(
|
||||||
&self,
|
&self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
|
|
|
@ -333,15 +333,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn obligation_for_op_method(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
trait_def_id: DefId,
|
||||||
|
self_ty: Ty<'tcx>,
|
||||||
|
opt_input_type: Option<Ty<'tcx>>,
|
||||||
|
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
|
||||||
|
{
|
||||||
|
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||||
|
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
|
||||||
|
match param.kind {
|
||||||
|
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
|
||||||
|
GenericParamDefKind::Type { .. } => {
|
||||||
|
if param.index == 0 {
|
||||||
|
return self_ty.into();
|
||||||
|
} else if let Some(input_type) = opt_input_type {
|
||||||
|
return input_type.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.var_for_def(span, param)
|
||||||
|
});
|
||||||
|
|
||||||
|
let trait_ref = ty::TraitRef::new(trait_def_id, substs);
|
||||||
|
|
||||||
|
// Construct an obligation
|
||||||
|
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
||||||
|
(
|
||||||
|
traits::Obligation::new(
|
||||||
|
traits::ObligationCause::new(
|
||||||
|
span,
|
||||||
|
self.body_id,
|
||||||
|
traits::BinOp {
|
||||||
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
|
is_lit: opt_input_expr
|
||||||
|
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
self.param_env,
|
||||||
|
poly_trait_ref.without_const().to_predicate(self.tcx),
|
||||||
|
),
|
||||||
|
substs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// `lookup_method_in_trait` is used for overloaded operators.
|
/// `lookup_method_in_trait` is used for overloaded operators.
|
||||||
/// It does a very narrow slice of what the normal probe/confirm path does.
|
/// It does a very narrow slice of what the normal probe/confirm path does.
|
||||||
/// In particular, it doesn't really do any probing: it simply constructs
|
/// In particular, it doesn't really do any probing: it simply constructs
|
||||||
/// an obligation for a particular trait with the given self type and checks
|
/// an obligation for a particular trait with the given self type and checks
|
||||||
/// whether that trait is implemented.
|
/// whether that trait is implemented.
|
||||||
//
|
|
||||||
// FIXME(#18741): it seems likely that we can consolidate some of this
|
|
||||||
// code with the other method-lookup code. In particular, the second half
|
|
||||||
// of this method is basically the same as confirmation.
|
|
||||||
#[instrument(level = "debug", skip(self, span, opt_input_types))]
|
#[instrument(level = "debug", skip(self, span, opt_input_types))]
|
||||||
pub(super) fn lookup_method_in_trait(
|
pub(super) fn lookup_method_in_trait(
|
||||||
&self,
|
&self,
|
||||||
|
@ -358,7 +400,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let (obligation, substs) =
|
let (obligation, substs) =
|
||||||
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
|
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
|
||||||
|
self.construct_obligation_for_trait(
|
||||||
|
span,
|
||||||
|
m_name,
|
||||||
|
trait_def_id,
|
||||||
|
obligation,
|
||||||
|
substs,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn lookup_op_method_in_trait(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
m_name: Ident,
|
||||||
|
trait_def_id: DefId,
|
||||||
|
self_ty: Ty<'tcx>,
|
||||||
|
opt_input_type: Option<Ty<'tcx>>,
|
||||||
|
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||||
|
let (obligation, substs) = self.obligation_for_op_method(
|
||||||
|
span,
|
||||||
|
trait_def_id,
|
||||||
|
self_ty,
|
||||||
|
opt_input_type,
|
||||||
|
opt_input_expr,
|
||||||
|
);
|
||||||
|
self.construct_obligation_for_trait(
|
||||||
|
span,
|
||||||
|
m_name,
|
||||||
|
trait_def_id,
|
||||||
|
obligation,
|
||||||
|
substs,
|
||||||
|
opt_input_expr,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(#18741): it seems likely that we can consolidate some of this
|
||||||
|
// code with the other method-lookup code. In particular, the second half
|
||||||
|
// of this method is basically the same as confirmation.
|
||||||
|
fn construct_obligation_for_trait(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
m_name: Ident,
|
||||||
|
trait_def_id: DefId,
|
||||||
|
obligation: traits::PredicateObligation<'tcx>,
|
||||||
|
substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
|
||||||
|
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
|
is_op: bool,
|
||||||
|
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||||
debug!(?obligation);
|
debug!(?obligation);
|
||||||
|
|
||||||
// Now we want to know if this can be matched
|
// Now we want to know if this can be matched
|
||||||
|
@ -395,8 +487,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
|
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig).0;
|
||||||
let fn_sig = fn_sig.subst(self.tcx, substs);
|
let fn_sig = fn_sig.subst(self.tcx, substs);
|
||||||
|
|
||||||
let InferOk { value, obligations: o } =
|
let InferOk { value, obligations: o } = if is_op {
|
||||||
self.normalize_associated_types_in_as_infer_ok(span, fn_sig);
|
self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
|
||||||
|
} else {
|
||||||
|
self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
|
||||||
|
};
|
||||||
let fn_sig = {
|
let fn_sig = {
|
||||||
obligations.extend(o);
|
obligations.extend(o);
|
||||||
value
|
value
|
||||||
|
@ -412,8 +507,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
// any late-bound regions appearing in its bounds.
|
// any late-bound regions appearing in its bounds.
|
||||||
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
|
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
|
||||||
|
|
||||||
let InferOk { value, obligations: o } =
|
let InferOk { value, obligations: o } = if is_op {
|
||||||
self.normalize_associated_types_in_as_infer_ok(span, bounds);
|
self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
|
||||||
|
} else {
|
||||||
|
self.normalize_associated_types_in_as_infer_ok(span, bounds)
|
||||||
|
};
|
||||||
let bounds = {
|
let bounds = {
|
||||||
obligations.extend(o);
|
obligations.extend(o);
|
||||||
value
|
value
|
||||||
|
@ -421,7 +519,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
assert!(!bounds.has_escaping_bound_vars());
|
assert!(!bounds.has_escaping_bound_vars());
|
||||||
|
|
||||||
let cause = traits::ObligationCause::misc(span, self.body_id);
|
let cause = if is_op {
|
||||||
|
ObligationCause::new(
|
||||||
|
span,
|
||||||
|
self.body_id,
|
||||||
|
traits::BinOp {
|
||||||
|
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||||
|
is_lit: opt_input_expr
|
||||||
|
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
traits::ObligationCause::misc(span, self.body_id)
|
||||||
|
};
|
||||||
obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
|
obligations.extend(traits::predicates_for_generics(cause.clone(), self.param_env, bounds));
|
||||||
|
|
||||||
// Also add an obligation for the method type being well-formed.
|
// Also add an obligation for the method type being well-formed.
|
||||||
|
|
|
@ -201,7 +201,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
span: rhs_expr.span,
|
span: rhs_expr.span,
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = self.lookup_op_method(lhs_ty, &[rhs_ty_var], Op::Binary(op, is_assign));
|
let result = self.lookup_op_method(
|
||||||
|
lhs_ty,
|
||||||
|
Some(rhs_ty_var),
|
||||||
|
Some(rhs_expr),
|
||||||
|
Op::Binary(op, is_assign),
|
||||||
|
);
|
||||||
|
|
||||||
// see `NB` above
|
// see `NB` above
|
||||||
let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
|
let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr));
|
||||||
|
@ -382,6 +387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
lhs_expr.span,
|
lhs_expr.span,
|
||||||
lhs_ty,
|
lhs_ty,
|
||||||
rhs_ty,
|
rhs_ty,
|
||||||
|
rhs_expr,
|
||||||
op,
|
op,
|
||||||
is_assign,
|
is_assign,
|
||||||
);
|
);
|
||||||
|
@ -390,6 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_expr.span,
|
rhs_expr.span,
|
||||||
rhs_ty,
|
rhs_ty,
|
||||||
lhs_ty,
|
lhs_ty,
|
||||||
|
lhs_expr,
|
||||||
op,
|
op,
|
||||||
is_assign,
|
is_assign,
|
||||||
);
|
);
|
||||||
|
@ -400,7 +407,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
if let Ref(_, rty, _) = lhs_ty.kind() {
|
if let Ref(_, rty, _) = lhs_ty.kind() {
|
||||||
if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
|
if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
|
||||||
&& self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok()
|
&& self
|
||||||
|
.lookup_op_method(
|
||||||
|
*rty,
|
||||||
|
Some(rhs_ty),
|
||||||
|
Some(rhs_expr),
|
||||||
|
Op::Binary(op, is_assign),
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
{
|
{
|
||||||
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
|
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
|
||||||
let msg = &format!(
|
let msg = &format!(
|
||||||
|
@ -443,7 +457,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let needs_bound = self
|
let needs_bound = self
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
eraser.fold_ty(lhs_ty),
|
eraser.fold_ty(lhs_ty),
|
||||||
&[eraser.fold_ty(rhs_ty)],
|
Some(eraser.fold_ty(rhs_ty)),
|
||||||
|
Some(rhs_expr),
|
||||||
Op::Binary(op, is_assign),
|
Op::Binary(op, is_assign),
|
||||||
)
|
)
|
||||||
.is_ok();
|
.is_ok();
|
||||||
|
@ -487,6 +502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
span: Span,
|
span: Span,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
other_ty: Ty<'tcx>,
|
other_ty: Ty<'tcx>,
|
||||||
|
other_expr: &'tcx hir::Expr<'tcx>,
|
||||||
op: hir::BinOp,
|
op: hir::BinOp,
|
||||||
is_assign: IsAssign,
|
is_assign: IsAssign,
|
||||||
) -> bool /* did we suggest to call a function because of missing parentheses? */ {
|
) -> bool /* did we suggest to call a function because of missing parentheses? */ {
|
||||||
|
@ -513,7 +529,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign))
|
.lookup_op_method(
|
||||||
|
fn_sig.output(),
|
||||||
|
Some(other_ty),
|
||||||
|
Some(other_expr),
|
||||||
|
Op::Binary(op, is_assign),
|
||||||
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
|
let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
|
||||||
|
@ -631,7 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
op: hir::UnOp,
|
op: hir::UnOp,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
assert!(op.is_by_value());
|
assert!(op.is_by_value());
|
||||||
match self.lookup_op_method(operand_ty, &[], Op::Unary(op, ex.span)) {
|
match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span)) {
|
||||||
Ok(method) => {
|
Ok(method) => {
|
||||||
self.write_method_call(ex.hir_id, method);
|
self.write_method_call(ex.hir_id, method);
|
||||||
method.sig.output()
|
method.sig.output()
|
||||||
|
@ -705,7 +726,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
fn lookup_op_method(
|
fn lookup_op_method(
|
||||||
&self,
|
&self,
|
||||||
lhs_ty: Ty<'tcx>,
|
lhs_ty: Ty<'tcx>,
|
||||||
other_tys: &[Ty<'tcx>],
|
other_ty: Option<Ty<'tcx>>,
|
||||||
|
other_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||||
op: Op,
|
op: Op,
|
||||||
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||||
let lang = self.tcx.lang_items();
|
let lang = self.tcx.lang_items();
|
||||||
|
@ -791,7 +813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let opname = Ident::with_dummy_span(opname);
|
let opname = Ident::with_dummy_span(opname);
|
||||||
let method = trait_did.and_then(|trait_did| {
|
let method = trait_did.and_then(|trait_did| {
|
||||||
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
|
self.lookup_op_method_in_trait(span, opname, trait_did, lhs_ty, other_ty, other_ty_expr)
|
||||||
});
|
});
|
||||||
|
|
||||||
match (method, trait_did) {
|
match (method, trait_did) {
|
||||||
|
@ -803,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
(None, None) => Err(vec![]),
|
(None, None) => Err(vec![]),
|
||||||
(None, Some(trait_did)) => {
|
(None, Some(trait_did)) => {
|
||||||
let (obligation, _) =
|
let (obligation, _) =
|
||||||
self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys));
|
self.obligation_for_op_method(span, trait_did, lhs_ty, other_ty, other_ty_expr);
|
||||||
let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
|
let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx);
|
||||||
fulfill.register_predicate_obligation(self, obligation);
|
fulfill.register_predicate_obligation(self, obligation);
|
||||||
Err(fulfill.select_where_possible(&self.infcx))
|
Err(fulfill.select_where_possible(&self.infcx))
|
||||||
|
|
|
@ -5,6 +5,10 @@ LL | 1.0f64 - 1
|
||||||
| ^ no implementation for `f64 - {integer}`
|
| ^ no implementation for `f64 - {integer}`
|
||||||
|
|
|
|
||||||
= help: the trait `Sub<{integer}>` is not implemented for `f64`
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | 1.0f64 - 1.0
|
||||||
|
| ++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
53
src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs
Normal file
53
src/test/ui/numbers-arithmetic/not-suggest-float-literal.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
fn add_float_to_integer(x: u8) -> f32 {
|
||||||
|
x + 100.0 //~ ERROR cannot add `{float}` to `u8`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_str_to_float(x: f64) -> f64 {
|
||||||
|
x + "foo" //~ ERROR cannot add `&str` to `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_lvar_to_float(x: f64) -> f64 {
|
||||||
|
let y = 3;
|
||||||
|
x + y //~ ERROR cannot add `{integer}` to `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_float_from_integer(x: u8) -> f32 {
|
||||||
|
x - 100.0 //~ ERROR cannot subtract `{float}` from `u8`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_str_from_f64(x: f64) -> f64 {
|
||||||
|
x - "foo" //~ ERROR cannot subtract `&str` from `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_lvar_from_f64(x: f64) -> f64 {
|
||||||
|
let y = 3;
|
||||||
|
x - y //~ ERROR cannot subtract `{integer}` from `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_integer_by_float(x: u8) -> f32 {
|
||||||
|
x * 100.0 //~ ERROR cannot multiply `u8` by `{float}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f64_by_str(x: f64) -> f64 {
|
||||||
|
x * "foo" //~ ERROR cannot multiply `f64` by `&str`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f64_by_lvar(x: f64) -> f64 {
|
||||||
|
let y = 3;
|
||||||
|
x * y //~ ERROR cannot multiply `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_integer_by_float(x: u8) -> u8 {
|
||||||
|
x / 100.0 //~ ERROR cannot divide `u8` by `{float}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f64_by_str(x: f64) -> f64 {
|
||||||
|
x / "foo" //~ ERROR cannot divide `f64` by `&str`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f64_by_lvar(x: f64) -> f64 {
|
||||||
|
let y = 3;
|
||||||
|
x / y //~ ERROR cannot divide `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,99 @@
|
||||||
|
error[E0277]: cannot add `{float}` to `u8`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:2:7
|
||||||
|
|
|
||||||
|
LL | x + 100.0
|
||||||
|
| ^ no implementation for `u8 + {float}`
|
||||||
|
|
|
||||||
|
= help: the trait `Add<{float}>` is not implemented for `u8`
|
||||||
|
|
||||||
|
error[E0277]: cannot add `&str` to `f64`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:6:7
|
||||||
|
|
|
||||||
|
LL | x + "foo"
|
||||||
|
| ^ no implementation for `f64 + &str`
|
||||||
|
|
|
||||||
|
= help: the trait `Add<&str>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot add `{integer}` to `f64`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:11:7
|
||||||
|
|
|
||||||
|
LL | x + y
|
||||||
|
| ^ no implementation for `f64 + {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Add<{integer}>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot subtract `{float}` from `u8`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:15:7
|
||||||
|
|
|
||||||
|
LL | x - 100.0
|
||||||
|
| ^ no implementation for `u8 - {float}`
|
||||||
|
|
|
||||||
|
= help: the trait `Sub<{float}>` is not implemented for `u8`
|
||||||
|
|
||||||
|
error[E0277]: cannot subtract `&str` from `f64`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:19:7
|
||||||
|
|
|
||||||
|
LL | x - "foo"
|
||||||
|
| ^ no implementation for `f64 - &str`
|
||||||
|
|
|
||||||
|
= help: the trait `Sub<&str>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot subtract `{integer}` from `f64`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:24:7
|
||||||
|
|
|
||||||
|
LL | x - y
|
||||||
|
| ^ no implementation for `f64 - {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot multiply `u8` by `{float}`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:28:7
|
||||||
|
|
|
||||||
|
LL | x * 100.0
|
||||||
|
| ^ no implementation for `u8 * {float}`
|
||||||
|
|
|
||||||
|
= help: the trait `Mul<{float}>` is not implemented for `u8`
|
||||||
|
|
||||||
|
error[E0277]: cannot multiply `f64` by `&str`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:32:7
|
||||||
|
|
|
||||||
|
LL | x * "foo"
|
||||||
|
| ^ no implementation for `f64 * &str`
|
||||||
|
|
|
||||||
|
= help: the trait `Mul<&str>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot multiply `f64` by `{integer}`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:37:7
|
||||||
|
|
|
||||||
|
LL | x * y
|
||||||
|
| ^ no implementation for `f64 * {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Mul<{integer}>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot divide `u8` by `{float}`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:41:7
|
||||||
|
|
|
||||||
|
LL | x / 100.0
|
||||||
|
| ^ no implementation for `u8 / {float}`
|
||||||
|
|
|
||||||
|
= help: the trait `Div<{float}>` is not implemented for `u8`
|
||||||
|
|
||||||
|
error[E0277]: cannot divide `f64` by `&str`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:45:7
|
||||||
|
|
|
||||||
|
LL | x / "foo"
|
||||||
|
| ^ no implementation for `f64 / &str`
|
||||||
|
|
|
||||||
|
= help: the trait `Div<&str>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error[E0277]: cannot divide `f64` by `{integer}`
|
||||||
|
--> $DIR/not-suggest-float-literal.rs:50:7
|
||||||
|
|
|
||||||
|
LL | x / y
|
||||||
|
| ^ no implementation for `f64 / {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Div<{integer}>` is not implemented for `f64`
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
37
src/test/ui/numbers-arithmetic/suggest-float-literal.fixed
Normal file
37
src/test/ui/numbers-arithmetic/suggest-float-literal.fixed
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
fn add_integer_to_f32(x: f32) -> f32 {
|
||||||
|
x + 100.0 //~ ERROR cannot add `{integer}` to `f32`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_integer_to_f64(x: f64) -> f64 {
|
||||||
|
x + 100.0 //~ ERROR cannot add `{integer}` to `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_integer_from_f32(x: f32) -> f32 {
|
||||||
|
x - 100.0 //~ ERROR cannot subtract `{integer}` from `f32`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_integer_from_f64(x: f64) -> f64 {
|
||||||
|
x - 100.0 //~ ERROR cannot subtract `{integer}` from `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f32_by_integer(x: f32) -> f32 {
|
||||||
|
x * 100.0 //~ ERROR cannot multiply `f32` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f64_by_integer(x: f64) -> f64 {
|
||||||
|
x * 100.0 //~ ERROR cannot multiply `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f32_by_integer(x: f32) -> f32 {
|
||||||
|
x / 100.0 //~ ERROR cannot divide `f32` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f64_by_integer(x: f64) -> f64 {
|
||||||
|
x / 100.0 //~ ERROR cannot divide `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
37
src/test/ui/numbers-arithmetic/suggest-float-literal.rs
Normal file
37
src/test/ui/numbers-arithmetic/suggest-float-literal.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// run-rustfix
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
fn add_integer_to_f32(x: f32) -> f32 {
|
||||||
|
x + 100 //~ ERROR cannot add `{integer}` to `f32`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_integer_to_f64(x: f64) -> f64 {
|
||||||
|
x + 100 //~ ERROR cannot add `{integer}` to `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_integer_from_f32(x: f32) -> f32 {
|
||||||
|
x - 100 //~ ERROR cannot subtract `{integer}` from `f32`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subtract_integer_from_f64(x: f64) -> f64 {
|
||||||
|
x - 100 //~ ERROR cannot subtract `{integer}` from `f64`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f32_by_integer(x: f32) -> f32 {
|
||||||
|
x * 100 //~ ERROR cannot multiply `f32` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn multiply_f64_by_integer(x: f64) -> f64 {
|
||||||
|
x * 100 //~ ERROR cannot multiply `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f32_by_integer(x: f32) -> f32 {
|
||||||
|
x / 100 //~ ERROR cannot divide `f32` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divide_f64_by_integer(x: f64) -> f64 {
|
||||||
|
x / 100 //~ ERROR cannot divide `f64` by `{integer}`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
99
src/test/ui/numbers-arithmetic/suggest-float-literal.stderr
Normal file
99
src/test/ui/numbers-arithmetic/suggest-float-literal.stderr
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
error[E0277]: cannot add `{integer}` to `f32`
|
||||||
|
--> $DIR/suggest-float-literal.rs:6:7
|
||||||
|
|
|
||||||
|
LL | x + 100
|
||||||
|
| ^ no implementation for `f32 + {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Add<{integer}>` is not implemented for `f32`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x + 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot add `{integer}` to `f64`
|
||||||
|
--> $DIR/suggest-float-literal.rs:10:7
|
||||||
|
|
|
||||||
|
LL | x + 100
|
||||||
|
| ^ no implementation for `f64 + {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Add<{integer}>` is not implemented for `f64`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x + 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot subtract `{integer}` from `f32`
|
||||||
|
--> $DIR/suggest-float-literal.rs:14:7
|
||||||
|
|
|
||||||
|
LL | x - 100
|
||||||
|
| ^ no implementation for `f32 - {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Sub<{integer}>` is not implemented for `f32`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x - 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot subtract `{integer}` from `f64`
|
||||||
|
--> $DIR/suggest-float-literal.rs:18:7
|
||||||
|
|
|
||||||
|
LL | x - 100
|
||||||
|
| ^ no implementation for `f64 - {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Sub<{integer}>` is not implemented for `f64`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x - 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot multiply `f32` by `{integer}`
|
||||||
|
--> $DIR/suggest-float-literal.rs:22:7
|
||||||
|
|
|
||||||
|
LL | x * 100
|
||||||
|
| ^ no implementation for `f32 * {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Mul<{integer}>` is not implemented for `f32`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x * 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot multiply `f64` by `{integer}`
|
||||||
|
--> $DIR/suggest-float-literal.rs:26:7
|
||||||
|
|
|
||||||
|
LL | x * 100
|
||||||
|
| ^ no implementation for `f64 * {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Mul<{integer}>` is not implemented for `f64`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x * 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot divide `f32` by `{integer}`
|
||||||
|
--> $DIR/suggest-float-literal.rs:30:7
|
||||||
|
|
|
||||||
|
LL | x / 100
|
||||||
|
| ^ no implementation for `f32 / {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Div<{integer}>` is not implemented for `f32`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x / 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error[E0277]: cannot divide `f64` by `{integer}`
|
||||||
|
--> $DIR/suggest-float-literal.rs:34:7
|
||||||
|
|
|
||||||
|
LL | x / 100
|
||||||
|
| ^ no implementation for `f64 / {integer}`
|
||||||
|
|
|
||||||
|
= help: the trait `Div<{integer}>` is not implemented for `f64`
|
||||||
|
help: consider using a floating-point literal by writing it with `.0`
|
||||||
|
|
|
||||||
|
LL | x / 100.0
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: aborting due to 8 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Reference in a new issue