Detect negative literal inferred to unsigned integer

```
error[E0277]: the trait bound `usize: Neg` is not satisfied
  --> $DIR/negative-literal-infered-to-unsigned.rs:2:14
   |
LL |     for x in -5..5 {
   |              ^^ the trait `Neg` is not implemented for `usize`
   |
help: consider specifying an integer type that can be negative
   |
LL |     for x in -5isize..5 {
   |                +++++
```
This commit is contained in:
Esteban Küber 2025-08-30 23:28:46 +00:00
parent fe55364329
commit 18a36bccf5
3 changed files with 78 additions and 1 deletions

View file

@ -3,7 +3,8 @@ use std::borrow::Cow;
use std::path::PathBuf;
use rustc_abi::ExternAbi;
use rustc_ast::TraitObjectSyntax;
use rustc_ast::ast::LitKind;
use rustc_ast::{LitIntType, TraitObjectSyntax};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*;
@ -280,6 +281,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
(suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
}
suggested |= self.detect_negative_literal(
&obligation,
main_trait_predicate,
&mut err,
);
if let Some(ret_span) = self.return_type_span(&obligation) {
if is_try_conversion {
let ty = self.tcx.short_string(
@ -950,6 +957,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
Ok(())
}
fn detect_negative_literal(
&self,
obligation: &PredicateObligation<'tcx>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
err: &mut Diag<'_>,
) -> bool {
if let ObligationCauseCode::BinOp { lhs_hir_id, .. } = obligation.cause.code()
&& let hir::Node::Expr(expr) = self.tcx.hir_node(*lhs_hir_id)
&& let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = expr.kind
&& let hir::ExprKind::Lit(lit) = inner.kind
&& let LitKind::Int(_, LitIntType::Unsuffixed) = lit.node
{
err.span_suggestion_verbose(
lit.span.shrink_to_hi(),
"consider specifying an integer type that can be negative",
match trait_pred.skip_binder().self_ty().kind() {
ty::Uint(ty::UintTy::Usize) => "isize",
ty::Uint(ty::UintTy::U8) => "i8",
ty::Uint(ty::UintTy::U16) => "i16",
ty::Uint(ty::UintTy::U32) => "i32",
ty::Uint(ty::UintTy::U64) => "i64",
ty::Uint(ty::UintTy::U128) => "i128",
_ => "i64",
}
.to_string(),
Applicability::MaybeIncorrect,
);
return true;
}
false
}
/// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
/// identify those method chain sub-expressions that could or could not have been annotated
/// with `?`.

View file

@ -0,0 +1,13 @@
fn main() {
for x in -5..5 {
//~^ ERROR: the trait bound `usize: Neg` is not satisfied
//~| HELP: consider specifying an integer type that can be negative
do_something(x);
}
let x = -5;
//~^ ERROR: the trait bound `usize: Neg` is not satisfied
//~| HELP: consider specifying an integer type that can be negative
do_something(x);
}
fn do_something(_val: usize) {}

View file

@ -0,0 +1,25 @@
error[E0277]: the trait bound `usize: Neg` is not satisfied
--> $DIR/negative-literal-infered-to-unsigned.rs:2:14
|
LL | for x in -5..5 {
| ^^ the trait `Neg` is not implemented for `usize`
|
help: consider specifying an integer type that can be negative
|
LL | for x in -5isize..5 {
| +++++
error[E0277]: the trait bound `usize: Neg` is not satisfied
--> $DIR/negative-literal-infered-to-unsigned.rs:7:13
|
LL | let x = -5;
| ^^ the trait `Neg` is not implemented for `usize`
|
help: consider specifying an integer type that can be negative
|
LL | let x = -5isize;
| +++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.