Skip to content

Commit

Permalink
Auto merge of rust-lang#7446 - Y-Nak:fix-7445, r=xFrednet,flip1995
Browse files Browse the repository at this point in the history
`default_numeric_fallback`: Fix FP with floating literal

Fix rust-lang#7445

changelog: `default_numeric_fallback`: Fix FP with floating literal
  • Loading branch information
bors committed Jul 13, 2021
2 parents bf4512e + 25e4c7d commit 8131445
Show file tree
Hide file tree
Showing 8 changed files with 738 additions and 43 deletions.
26 changes: 18 additions & 8 deletions clippy_lints/src/default_numeric_fallback.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::numeric_literal;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
Expand Down Expand Up @@ -78,16 +79,25 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
if !ty_bound.is_integral();
if !ty_bound.is_numeric();
then {
let suffix = match lit_ty.kind() {
ty::Int(IntTy::I32) => "i32",
ty::Float(FloatTy::F64) => "f64",
let (suffix, is_float) = match lit_ty.kind() {
ty::Int(IntTy::I32) => ("i32", false),
ty::Float(FloatTy::F64) => ("f64", true),
// Default numeric fallback never results in other types.
_ => return,
};

let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix);
let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
src
} else {
match lit.node {
LitKind::Int(src, _) => format!("{}", src),
LitKind::Float(src, _) => format!("{}", src),
_ => return,
}
};
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
span_lint_and_sugg(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
Expand Down Expand Up @@ -219,10 +229,10 @@ enum TyBound<'tcx> {
}

impl<'tcx> TyBound<'tcx> {
fn is_integral(self) -> bool {
fn is_numeric(self) -> bool {
match self {
TyBound::Any => true,
TyBound::Ty(t) => t.is_integral(),
TyBound::Ty(t) => t.is_numeric(),
TyBound::Nothing => false,
}
}
Expand Down
3 changes: 3 additions & 0 deletions clippy_utils/src/numeric_literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ impl<'a> NumericLiteral<'a> {
}

if let Some(suffix) = self.suffix {
if output.ends_with('.') {
output.push('0');
}
output.push('_');
output.push_str(suffix);
}
Expand Down
174 changes: 174 additions & 0 deletions tests/ui/default_numeric_fallback_f64.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// run-rustfix
// aux-build:macro_rules.rs

#![warn(clippy::default_numeric_fallback)]
#![allow(unused)]
#![allow(clippy::never_loop)]
#![allow(clippy::no_effect)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::branches_sharing_code)]
#![allow(clippy::match_single_binding)]

#[macro_use]
extern crate macro_rules;

mod basic_expr {
fn test() {
// Should lint unsuffixed literals typed `f64`.
let x = 0.12_f64;
let x = [1.0_f64, 2.0_f64, 3.0_f64];
let x = if true { (1.0_f64, 2.0_f64) } else { (3.0_f64, 4.0_f64) };
let x = match 1.0_f64 {
_ => 1.0_f64,
};

// Should NOT lint suffixed literals.
let x = 0.12_f64;

// Should NOT lint literals in init expr if `Local` has a type annotation.
let x: f64 = 0.1;
let x: [f64; 3] = [1., 2., 3.];
let x: (f64, f64) = if true { (1., 2.) } else { (3., 4.) };
let x: _ = 1.;
}
}

mod nested_local {
fn test() {
let x: _ = {
// Should lint this because this literal is not bound to any types.
let y = 1.0_f64;

// Should NOT lint this because this literal is bound to `_` of outer `Local`.
1.
};

let x: _ = if true {
// Should lint this because this literal is not bound to any types.
let y = 1.0_f64;

// Should NOT lint this because this literal is bound to `_` of outer `Local`.
1.
} else {
// Should lint this because this literal is not bound to any types.
let y = 1.0_f64;

// Should NOT lint this because this literal is bound to `_` of outer `Local`.
2.
};
}
}

mod function_def {
fn ret_f64() -> f64 {
// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
1.0_f64
}

fn test() {
// Should lint this because return type is inferred to `f64` and NOT bound to a concrete
// type.
let f = || -> _ { 1.0_f64 };

// Even though the output type is specified,
// this unsuffixed literal is linted to reduce heuristics and keep codebase simple.
let f = || -> f64 { 1.0_f64 };
}
}

mod function_calls {
fn concrete_arg(f: f64) {}

fn generic_arg<T>(t: T) {}

fn test() {
// Should NOT lint this because the argument type is bound to a concrete type.
concrete_arg(1.);

// Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
generic_arg(1.0_f64);

// Should lint this because the argument type is inferred to `f64` and NOT bound to a concrete type.
let x: _ = generic_arg(1.0_f64);
}
}

mod struct_ctor {
struct ConcreteStruct {
x: f64,
}

struct GenericStruct<T> {
x: T,
}

fn test() {
// Should NOT lint this because the field type is bound to a concrete type.
ConcreteStruct { x: 1. };

// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
GenericStruct { x: 1.0_f64 };

// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
let _ = GenericStruct { x: 1.0_f64 };
}
}

mod enum_ctor {
enum ConcreteEnum {
X(f64),
}

enum GenericEnum<T> {
X(T),
}

fn test() {
// Should NOT lint this because the field type is bound to a concrete type.
ConcreteEnum::X(1.);

// Should lint this because the field type is inferred to `f64` and NOT bound to a concrete type.
GenericEnum::X(1.0_f64);
}
}

mod method_calls {
struct StructForMethodCallTest {}

impl StructForMethodCallTest {
fn concrete_arg(&self, f: f64) {}

fn generic_arg<T>(&self, t: T) {}
}

fn test() {
let s = StructForMethodCallTest {};

// Should NOT lint this because the argument type is bound to a concrete type.
s.concrete_arg(1.);

// Should lint this because the argument type is bound to a concrete type.
s.generic_arg(1.0_f64);
}
}

mod in_macro {
macro_rules! internal_macro {
() => {
let x = 22.0_f64;
};
}

// Should lint in internal macro.
fn internal() {
internal_macro!();
}

// Should NOT lint in external macro.
fn external() {
default_numeric_fallback!();
}
}

fn main() {}
Loading

0 comments on commit 8131445

Please sign in to comment.