Skip to content

Commit

Permalink
Implement new Style/ExponentialNotation cop
Browse files Browse the repository at this point in the history
This cop implements the rule describe in the "exponential notation"
section of the ruby style guide.
It allows for an additional style not mentionned in the guide,
`integral` which allows only integer mantissa, without any
trailing zero.
  • Loading branch information
tdeo authored and bbatsov committed Apr 10, 2020
1 parent ae73ae7 commit 00e8170
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@
* [#7862](https://github.com/rubocop-hq/rubocop/issues/7862): Corrector now has a `wrap` method. ([@marcandre][])
* [#7850](https://github.com/rubocop-hq/rubocop/issues/7850): Make it possible to enable/disable pending cops. ([@koic][])
* [#7861](https://github.com/rubocop-hq/rubocop/issues/7861): Make it to allow `Style/CaseEquality` when the receiver is a constant. ([@rafaelfranca][])
* Add a new `Style/ExponentialNotation` cop. ([@tdeo][])

### Bug fixes

Expand Down
11 changes: 11 additions & 0 deletions config/default.yml
Expand Up @@ -2757,6 +2757,17 @@ Style/ExpandPathArguments:
Enabled: true
VersionAdded: '0.53'

Style/ExponentialNotation:
Description: 'When using exponential notation, favor a mantissa between 1 (inclusive) and 10 (exclusive).'
StyleGuide: '#exponential-notation'
Enabled: pending
VersionAdded: '0.82'
EnforcedStyle: scientific
SupportedStyles:
- scientific
- engineering
- integral

Style/FloatDivision:
Description: 'For performing float division, coerce one side only.'
StyleGuide: '#float-division'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -441,6 +441,7 @@
require_relative 'rubocop/cop/style/eval_with_location'
require_relative 'rubocop/cop/style/even_odd'
require_relative 'rubocop/cop/style/expand_path_arguments'
require_relative 'rubocop/cop/style/exponential_notation'
require_relative 'rubocop/cop/style/float_division'
require_relative 'rubocop/cop/style/for'
require_relative 'rubocop/cop/style/format_string'
Expand Down
119 changes: 119 additions & 0 deletions lib/rubocop/cop/style/exponential_notation.rb
@@ -0,0 +1,119 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop enforces consistency when using exponential notation
# for numbers in the code (eg 1.2e4). Different styles are supported:
# - `scientific` which enforces a mantissa between 1 (inclusive)
# and 10 (exclusive).
# - `engineering` which enforces the exponent to be a multiple of 3
# and the mantissa to be between 0.1 (inclusive)
# and 10 (exclusive).
# - `integral` which enforces the mantissa to always be a whole number
# without trailing zeroes.
#
# @example EnforcedStyle: scientific (default)
# # Enforces a mantissa between 1 (inclusive) and 10 (exclusive).
#
# # bad
# 10e6
# 0.3e4
# 11.7e5
# 3.14e0
#
# # good
# 1e7
# 3e3
# 1.17e6
# 3.14
#
# @example EnforcedStyle: engineering
# # Enforces using multiple of 3 exponents,
# # mantissa should be between 0.1 (inclusive) and 1000 (exclusive)
#
# # bad
# 3.2e7
# 0.1e5
# 12e5
# 1232e6
#
# # good
# 32e6
# 10e3
# 1.2e6
# 1.232e9
#
# @example EnforcedStyle: integral
# # Enforces the mantissa to have no decimal part and no
# # trailing zeroes.
#
# # bad
# 3.2e7
# 0.1e5
# 120e4
#
# # good
# 32e6
# 1e4
# 12e5
#
class ExponentialNotation < Cop
include ConfigurableEnforcedStyle

def on_float(node)
add_offense(node) if offense?(node)
end

private

def scientific?(node)
mantissa, = node.source.split('e')
mantissa =~ /^-?[1-9](\.\d*[0-9])?$/
end

def engineering?(node)
mantissa, exponent = node.source.split('e')
return false unless exponent =~ /^-?\d+$/
return false unless (exponent.to_i % 3).zero?
return false if mantissa =~ /^-?\d{4}/
return false if mantissa =~ /^-?0\d/
return false if mantissa =~ /^-?0.0/

true
end

def integral(node)
mantissa, = node.source.split('e')
mantissa =~ /^-?[1-9](\d*[1-9])?$/
end

def offense?(node)
return false unless node.source['e']

case style
when :scientific
!scientific?(node)
when :engineering
!engineering?(node)
when :integral
!integral(node)
else
false
end
end

def message(_node)
case style
when :scientific
'Use a mantissa in [1, 10[.'
when :engineering
'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.'
when :integral
'Use an integer as mantissa, without trailing zero.'
end
end
end
end
end
end
1 change: 1 addition & 0 deletions manual/cops.md
Expand Up @@ -352,6 +352,7 @@ In the following section you find all available cops:
* [Style/EvalWithLocation](cops_style.md#styleevalwithlocation)
* [Style/EvenOdd](cops_style.md#styleevenodd)
* [Style/ExpandPathArguments](cops_style.md#styleexpandpatharguments)
* [Style/ExponentialNotation](cops_style.md#styleexponentialnotation)
* [Style/FloatDivision](cops_style.md#stylefloatdivision)
* [Style/For](cops_style.md#stylefor)
* [Style/FormatString](cops_style.md#styleformatstring)
Expand Down
80 changes: 80 additions & 0 deletions manual/cops_style.md
Expand Up @@ -2101,6 +2101,86 @@ Pathname.new(__FILE__).parent.expand_path
Pathname.new(__dir__).expand_path
```

## Style/ExponentialNotation

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Pending | Yes | No | 0.82 | -

This cop enforces consistency when using exponential notation
for numbers in the code (eg 1.2e4). Different styles are supported:
- `scientific` which enforces a mantissa between 1 (inclusive)
and 10 (exclusive).
- `engineering` which enforces the exponent to be a multiple of 3
and the mantissa to be between 0.1 (inclusive)
and 10 (exclusive).
- `integral` which enforces the mantissa to always be a whole number
without trailing zeroes.

### Examples

#### EnforcedStyle: scientific (default)

```ruby
# Enforces a mantissa between 1 (inclusive) and 10 (exclusive).
# bad
10e6
0.3e4
11.7e5
3.14e0
# good
1e7
3e3
1.17e6
3.14
```
#### EnforcedStyle: engineering

```ruby
# Enforces using multiple of 3 exponents,
# mantissa should be between 0.1 (inclusive) and 1000 (exclusive)
# bad
3.2e7
0.1e5
12e5
1232e6
# good
32e6
10e3
1.2e6
1.232e9
```
#### EnforcedStyle: integral

```ruby
# Enforces the mantissa to have no decimal part and no
# trailing zeroes.
# bad
3.2e7
0.1e5
120e4
# good
32e6
1e4
12e5
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
EnforcedStyle | `scientific` | `scientific`, `engineering`, `integral`

### References

* [https://rubystyle.guide#exponential-notation](https://rubystyle.guide#exponential-notation)

## Style/FloatDivision

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down

0 comments on commit 00e8170

Please sign in to comment.