lib/rubocop/cop/style/exponential_notation.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# 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 < Base
include ConfigurableEnforcedStyle
MESSAGES = {
scientific: 'Use a mantissa in [1, 10[.',
engineering: 'Use an exponent divisible by 3 and a mantissa in [0.1, 1000[.',
integral: 'Use an integer as mantissa, without trailing zero.'
}.freeze
def on_float(node)
add_offense(node) if offense?(node)
end
private
def scientific?(node)
mantissa, = node.source.split('e')
/^-?[1-9](\.\d*[0-9])?$/.match?(mantissa)
end
def engineering?(node)
mantissa, exponent = node.source.split('e')
return false unless /^-?\d+$/.match?(exponent)
return false unless (exponent.to_i % 3).zero?
return false if /^-?\d{4}/.match?(mantissa)
return false if /^-?0\d/.match?(mantissa)
return false if /^-?0.0/.match?(mantissa)
true
end
def integral(node)
mantissa, = node.source.split('e')
/^-?[1-9](\d*[1-9])?$/.match?(mantissa)
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)
MESSAGES[style]
end
end
end
end
end