diff options
author | 🙈 jacopo beschi 🙉 <intrip@gmail.com> | 2018-04-18 09:19:40 +0000 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2018-04-18 09:19:40 +0000 |
commit | c6b1043e9d1b7fe9912c330b6e7d4342f2a9694e (patch) | |
tree | 45c3fd39c83122eb602f217501924bc8d6899987 /rubocop/cop/avoid_return_from_blocks.rb | |
parent | 3529ccae9e3a484da5a4fba32bfdf0317f289363 (diff) | |
download | gitlab-ce-c6b1043e9d1b7fe9912c330b6e7d4342f2a9694e.tar.gz |
Resolve "Make a Rubocop that forbids returning from a block"
Diffstat (limited to 'rubocop/cop/avoid_return_from_blocks.rb')
-rw-r--r-- | rubocop/cop/avoid_return_from_blocks.rb | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/rubocop/cop/avoid_return_from_blocks.rb b/rubocop/cop/avoid_return_from_blocks.rb new file mode 100644 index 00000000000..40b2aed019f --- /dev/null +++ b/rubocop/cop/avoid_return_from_blocks.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + # Checks for return inside blocks. + # For more information see: https://gitlab.com/gitlab-org/gitlab-ce/issues/42889 + # + # @example + # # bad + # call do + # return if something + # + # do_something_else + # end + # + # # good + # call do + # break if something + # + # do_something_else + # end + # + class AvoidReturnFromBlocks < RuboCop::Cop::Cop + MSG = 'Do not return from a block, use next or break instead.' + DEF_METHODS = %i[define_method lambda].freeze + WHITELISTED_METHODS = %i[each each_filename times loop].freeze + + def on_block(node) + block_body = node.body + + return unless block_body + return unless top_block?(node) + + block_body.each_node(:return) do |return_node| + next if parent_blocks(node, return_node).all?(&method(:whitelisted?)) + + add_offense(return_node) + end + end + + private + + def top_block?(node) + current_node = node + top_block = nil + + while current_node && current_node.type != :def + top_block = current_node if current_node.type == :block + current_node = current_node.parent + end + + top_block == node + end + + def parent_blocks(node, current_node) + blocks = [] + + until node == current_node || def?(current_node) + blocks << current_node if current_node.type == :block + current_node = current_node.parent + end + + blocks << node if node == current_node && !def?(node) + blocks + end + + def def?(node) + node.type == :def || node.type == :defs || + (node.type == :block && DEF_METHODS.include?(node.method_name)) + end + + def whitelisted?(block_node) + WHITELISTED_METHODS.include?(block_node.method_name) + end + end + end +end |