Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/new_add_new_cop_styleendlessmethod.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* [#9281](https://github.com/rubocop-hq/rubocop/pull/9281): Add new cop `Style/EndlessMethod`. ([@dvandersluis][])
11 changes: 11 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3196,6 +3196,17 @@ Style/EndBlock:
VersionAdded: '0.9'
VersionChanged: '0.81'

Style/EndlessMethod:
Description: 'Avoid the use of multi-lined endless method definitions.'
StyleGuide: '#endless-methods'
Enabled: pending
VersionAdded: <<next>>
EnforcedStyle: allow_single_line
SupportedStyles:
- allow_single_line
- allow_always
- disallow

Style/EvalWithLocation:
Description: 'Pass `__FILE__` and `__LINE__` to `eval` method, as they are used by backtraces.'
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@
require_relative 'rubocop/cop/style/empty_lambda_parameter'
require_relative 'rubocop/cop/style/empty_literal'
require_relative 'rubocop/cop/style/empty_method'
require_relative 'rubocop/cop/style/endless_method'
require_relative 'rubocop/cop/style/encoding'
require_relative 'rubocop/cop/style/end_block'
require_relative 'rubocop/cop/style/eval_with_location'
Expand Down
102 changes: 102 additions & 0 deletions lib/rubocop/cop/style/endless_method.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# This cop checks for endless methods.
#
# It can enforce either the use of endless methods definitions
# for single-lined method bodies, or disallow endless methods.
#
# Other method definition types are not considered by this cop.
#
# The supported styles are:
# * allow_single_line (default) - only single line endless method definitions are allowed.
# * allow_always - all endless method definitions are allowed.
# * disallow - all endless method definitions are disallowed.
#
# NOTE: Incorrect endless method definitions will always be
# corrected to a multi-line definition.
#
# @example EnforcedStyle: allow_single_line (default)
# # good
# def my_method() = x
#
# # bad, multi-line endless method
# def my_method() = x.foo
# .bar
# .baz
#
# @example EnforcedStyle: allow_always
# # good
# def my_method() = x
#
# # good
# def my_method() = x.foo
# .bar
# .baz
#
# @example EnforcedStyle: disallow
# # bad
# def my_method; x end
#
# # bad
# def my_method() = x.foo
# .bar
# .baz
#
class EndlessMethod < Base
include ConfigurableEnforcedStyle
extend TargetRubyVersion
extend AutoCorrector

minimum_target_ruby_version 3.0

CORRECTION_STYLES = %w[multiline single_line].freeze
MSG = 'Avoid endless method definitions.'
MSG_MULTI_LINE = 'Avoid endless method definitions with multiple lines.'

def on_def(node)
if style == :disallow
handle_disallow_style(node)
else
handle_allow_style(node)
end
end

private

def handle_allow_style(node)
return unless node.endless?
return if node.single_line? || style == :allow_always

add_offense(node, message: MSG_MULTI_LINE) do |corrector|
correct_to_multiline(corrector, node)
end
end

def handle_disallow_style(node)
return unless node.endless?

add_offense(node) do |corrector|
correct_to_multiline(corrector, node)
end
end

def correct_to_multiline(corrector, node)
replacement = <<~RUBY.strip
def #{node.method_name}#{arguments(node)}
#{node.body.source}
end
RUBY

corrector.replace(node, replacement)
end

def arguments(node, missing = '')
node.arguments.any? ? node.arguments.source : missing
end
end
end
end
end
142 changes: 142 additions & 0 deletions spec/rubocop/cop/style/endless_method_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Style::EndlessMethod, :config do
context 'Ruby >= 3.0', :ruby30 do
context 'EnforcedStyle: disallow' do
let(:cop_config) { { 'EnforcedStyle' => 'disallow' } }

it 'registers an offense for an endless method' do
expect_offense(<<~RUBY)
def my_method() = x
^^^^^^^^^^^^^^^^^^^ Avoid endless method definitions.
RUBY

expect_correction(<<~RUBY)
def my_method
x
end
RUBY
end

it 'registers an offense for an endless method with arguments' do
expect_offense(<<~RUBY)
def my_method(a, b) = x
^^^^^^^^^^^^^^^^^^^^^^^ Avoid endless method definitions.
RUBY

expect_correction(<<~RUBY)
def my_method(a, b)
x
end
RUBY
end
end

context 'EnforcedStyle: allow_single_line' do
let(:cop_config) { { 'EnforcedStyle' => 'allow_single_line' } }

it 'does not register an offense for an endless method' do
expect_no_offenses(<<~RUBY)
def my_method() = x
RUBY
end

it 'does not register an offense for an endless method with arguments' do
expect_no_offenses(<<~RUBY)
def my_method(a, b) = x
RUBY
end

it 'registers an offense and corrects for a multiline endless method' do
expect_offense(<<~RUBY)
def my_method() = x.foo
^^^^^^^^^^^^^^^^^^^^^^^ Avoid endless method definitions with multiple lines.
.bar
.baz
RUBY

expect_correction(<<~RUBY)
def my_method
x.foo
.bar
.baz
end
RUBY
end

it 'registers an offense and corrects for a multiline endless method with begin' do
expect_offense(<<~RUBY)
def my_method() = begin
^^^^^^^^^^^^^^^^^^^^^^^ Avoid endless method definitions with multiple lines.
foo && bar
end
RUBY

expect_correction(<<~RUBY)
def my_method
begin
foo && bar
end
end
RUBY
end

it 'registers an offense and corrects for a multiline endless method with arguments' do
expect_offense(<<~RUBY)
def my_method(a, b) = x.foo
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid endless method definitions with multiple lines.
.bar
.baz
RUBY

expect_correction(<<~RUBY)
def my_method(a, b)
x.foo
.bar
.baz
end
RUBY
end
end

context 'EnforcedStyle: allow_always' do
let(:cop_config) { { 'EnforcedStyle' => 'allow_always' } }

it 'does not register an offense for an endless method' do
expect_no_offenses(<<~RUBY)
def my_method() = x
RUBY
end

it 'does not register an offense for an endless method with arguments' do
expect_no_offenses(<<~RUBY)
def my_method(a, b) = x
RUBY
end

it 'registers an offense and corrects for a multiline endless method' do
expect_no_offenses(<<~RUBY)
def my_method() = x.foo
.bar
.baz
RUBY
end

it 'registers an offense and corrects for a multiline endless method with begin' do
expect_no_offenses(<<~RUBY)
def my_method() = begin
foo && bar
end
RUBY
end

it 'registers an offense and corrects for a multiline endless method with arguments' do
expect_no_offenses(<<~RUBY)
def my_method(a, b) = x.foo
.bar
.baz
RUBY
end
end
end
end