[refurb] Implement math-constant (FURB152) (#8727) · astral-sh/ruff@2faac1e (original) (raw)

``

1

`+

use anyhow::Result;

`

``

2

+

``

3

`+

use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};

`

``

4

`+

use ruff_macros::{derive_message_formats, violation};

`

``

5

`+

use ruff_python_ast::{self as ast, Number};

`

``

6

`+

use ruff_text_size::Ranged;

`

``

7

+

``

8

`+

use crate::checkers::ast::Checker;

`

``

9

`+

use crate::importer::ImportRequest;

`

``

10

+

``

11

`+

/// ## What it does

`

``

12

`` +

/// Checks for literals that are similar to constants in math module.

``

``

13

`+

///

`

``

14

`+

/// ## Why is this bad?

`

``

15

`+

/// Hard-coding mathematical constants like π increases code duplication,

`

``

16

`+

/// reduces readability, and may lead to a lack of precision.

`

``

17

`+

///

`

``

18

`+

/// ## Example

`

``

19


/// ```python

``

20

`+

/// A = 3.141592 * r**2

`

``

21


/// ```

``

22

`+

///

`

``

23

`+

/// Use instead:

`

``

24


/// ```python

``

25

`+

/// A = math.pi * r**2

`

``

26


/// ```

``

27

`+

///

`

``

28

`+

/// ## References

`

``

29

`` +

/// - Python documentation: math constants

``

``

30

`+

#[violation]

`

``

31

`+

pub struct MathConstant {

`

``

32

`+

literal: String,

`

``

33

`+

constant: &'static str,

`

``

34

`+

}

`

``

35

+

``

36

`+

impl Violation for MathConstant {

`

``

37

`+

const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;

`

``

38

+

``

39

`+

#[derive_message_formats]

`

``

40

`+

fn message(&self) -> String {

`

``

41

`+

let MathConstant { literal, constant } = self;

`

``

42

`` +

format!("Replace {literal} with math.{constant}")

``

``

43

`+

}

`

``

44

+

``

45

`+

fn fix_title(&self) -> Option {

`

``

46

`+

let MathConstant { constant, .. } = self;

`

``

47

`` +

Some(format!("Use math.{constant}"))

``

``

48

`+

}

`

``

49

`+

}

`

``

50

+

``

51

`+

/// FURB152

`

``

52

`+

pub(crate) fn math_constant(checker: &mut Checker, literal: &ast::ExprNumberLiteral) {

`

``

53

`+

let Number::Float(value) = literal.value else {

`

``

54

`+

return;

`

``

55

`+

};

`

``

56

`+

for (real_value, constant) in [

`

``

57

`+

(std::f64::consts::PI, "pi"),

`

``

58

`+

(std::f64::consts::E, "e"),

`

``

59

`+

(std::f64::consts::TAU, "tau"),

`

``

60

`+

] {

`

``

61

`+

if (value - real_value).abs() < 1e-2 {

`

``

62

`+

let mut diagnostic = Diagnostic::new(

`

``

63

`+

MathConstant {

`

``

64

`+

literal: checker.locator().slice(literal).into(),

`

``

65

`+

constant,

`

``

66

`+

},

`

``

67

`+

literal.range(),

`

``

68

`+

);

`

``

69

`+

diagnostic.try_set_fix(|| convert_to_constant(literal, constant, checker));

`

``

70

`+

checker.diagnostics.push(diagnostic);

`

``

71

`+

return;

`

``

72

`+

}

`

``

73

`+

}

`

``

74

`+

}

`

``

75

+

``

76

`+

fn convert_to_constant(

`

``

77

`+

literal: &ast::ExprNumberLiteral,

`

``

78

`+

constant: &'static str,

`

``

79

`+

checker: &Checker,

`

``

80

`+

) -> Result {

`

``

81

`+

let (edit, binding) = checker.importer().get_or_import_symbol(

`

``

82

`+

&ImportRequest::import("math", constant),

`

``

83

`+

literal.start(),

`

``

84

`+

checker.semantic(),

`

``

85

`+

)?;

`

``

86

`+

Ok(Fix::safe_edits(

`

``

87

`+

Edit::range_replacement(binding, literal.range()),

`

``

88

`+

[edit],

`

``

89

`+

))

`

``

90

`+

}

`