Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a408b5b
commit 41928e8
Showing
5 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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][]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |