-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3636 from tessi/feature/3570-guard-clauses-on-mul…
…ti-line-statements [Fix #3570] avoid if-modifiers on multi-line statements
- Loading branch information
Showing
8 changed files
with
241 additions
and
36 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
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
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,73 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module Style | ||
# Checks for uses of if/unless modifiers with multiple-lines bodies. | ||
# | ||
# @example | ||
# | ||
# # bad | ||
# { | ||
# result: 'this should not happen' | ||
# } unless cond | ||
# | ||
# # good | ||
# { result: 'ok' } if cond | ||
class MultilineIfModifier < Cop | ||
include IfNode | ||
include StatementModifier | ||
include AutocorrectAlignment | ||
|
||
MSG = 'Favor a normal %s-statement over a modifier' \ | ||
' clause in a multiline statement.'.freeze | ||
|
||
def on_if(node) | ||
return unless modifier_if?(node) | ||
|
||
_cond, body = if_node_parts(node) | ||
return if body.single_line? | ||
|
||
add_offense(node, :expression) | ||
end | ||
|
||
private | ||
|
||
def message(node) | ||
format(MSG, node.loc.keyword.source) | ||
end | ||
|
||
def autocorrect(node) | ||
lambda do |corrector| | ||
corrector.replace(node.source_range, to_normal_if(node)) | ||
end | ||
end | ||
|
||
def to_normal_if(node) | ||
cond, body = if_node_parts(node) | ||
indented_body = indented_body(body, node) | ||
|
||
condition = "#{node.loc.keyword.source} #{cond.source}" | ||
indented_end = "#{offset(node)}end" | ||
|
||
"#{condition}\n#{indented_body}\n#{indented_end}" | ||
end | ||
|
||
def configured_indentation_width | ||
super || 2 | ||
end | ||
|
||
def indented_body(body, node) | ||
body_source = "#{offset(node)}#{body.source}" | ||
body_source.each_line.map do |line| | ||
if line == "\n" | ||
line | ||
else | ||
line.sub(/^\s{#{offset(node).length}}/, indentation(node)) | ||
end | ||
end.join | ||
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,116 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
describe RuboCop::Cop::Style::MultilineIfModifier do | ||
subject(:cop) { described_class.new } | ||
|
||
shared_examples 'offense' do |modifier| | ||
it 'registers an offense' do | ||
inspect_source(cop, source) | ||
expect(cop.messages) | ||
.to eq(["Favor a normal #{modifier}-statement over a modifier" \ | ||
' clause in a multiline statement.']) | ||
end | ||
end | ||
|
||
shared_examples 'no offense' do | ||
it 'does not register an offense' do | ||
inspect_source(cop, source) | ||
expect(cop.messages).to be_empty | ||
end | ||
end | ||
|
||
shared_examples 'autocorrect' do |correct_code| | ||
it 'auto-corrects' do | ||
corrected = autocorrect_source(cop, source) | ||
expect(corrected).to eq(correct_code) | ||
end | ||
end | ||
|
||
context 'if guard clause' do | ||
let(:source) do | ||
[ | ||
'{', | ||
' result: run', | ||
'} if cond' | ||
].join("\n") | ||
end | ||
|
||
include_examples 'offense', 'if' | ||
include_examples 'autocorrect', "if cond\n {\n result: run\n }\nend" | ||
|
||
context 'one liner' do | ||
let(:source) { 'run if cond' } | ||
|
||
include_examples 'no offense' | ||
end | ||
|
||
context 'multiline condition' do | ||
let(:source) { "run if cond &&\n cond2" } | ||
|
||
include_examples 'no offense' | ||
end | ||
|
||
context 'indented offense' do | ||
let(:source) do | ||
[ | ||
' {', | ||
' result: run', | ||
' } if cond' | ||
].join("\n") | ||
end | ||
|
||
include_examples 'autocorrect', " if cond\n" \ | ||
" {\n" \ | ||
" result: run\n" \ | ||
" }\n" \ | ||
' end' | ||
end | ||
end | ||
|
||
context 'unless guard clause' do | ||
let(:source) do | ||
[ | ||
'{', | ||
' result: run', | ||
'} unless cond' | ||
].join("\n") | ||
end | ||
|
||
include_examples 'offense', 'unless' | ||
include_examples 'autocorrect', "unless cond\n" \ | ||
" {\n" \ | ||
" result: run\n" \ | ||
" }\n" \ | ||
'end' | ||
|
||
context 'one liner' do | ||
let(:source) { 'run unless cond' } | ||
|
||
include_examples 'no offense' | ||
end | ||
|
||
context 'multiline condition' do | ||
let(:source) { "run unless cond &&\n cond2" } | ||
|
||
include_examples 'no offense' | ||
end | ||
|
||
context 'indented offense' do | ||
let(:source) do | ||
[ | ||
' {', | ||
' result: run', | ||
' } unless cond' | ||
].join("\n") | ||
end | ||
|
||
include_examples 'autocorrect', " unless cond\n" \ | ||
" {\n" \ | ||
" result: run\n" \ | ||
" }\n" \ | ||
' 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
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