class RuboCop::Cop::Style::BlockDelimiters
Check for uses of braces or do/end around single line or multi-line blocks.
Methods that can be either procedural or functional and cannot be categorised from their usage alone is ignored. ‘lambda`, `proc`, and `it` are their defaults. Additional methods can be added to the `AllowedMethods`.
@example EnforcedStyle: line_count_based (default)
# bad - single line block items.each do |item| item / 5 end # good - single line block items.each { |item| item / 5 } # bad - multi-line block things.map { |thing| something = thing.some_method process(something) } # good - multi-line block things.map do |thing| something = thing.some_method process(something) end
@example EnforcedStyle: semantic
# Prefer `do...end` over `{...}` for procedural blocks. # return value is used/assigned # bad foo = map do |x| x end puts (map do |x| x end) # return value is not used out of scope # good map do |x| x end # Prefer `{...}` over `do...end` for functional blocks. # return value is not used out of scope # bad each { |x| x } # return value is used/assigned # good foo = map { |x| x } map { |x| x }.inspect # The AllowBracesOnProceduralOneLiners option is allowed unless the # EnforcedStyle is set to `semantic`. If so: # If the AllowBracesOnProceduralOneLiners option is unspecified, or # set to `false` or any other falsey value, then semantic purity is # maintained, so one-line procedural blocks must use do-end, not # braces. # bad collection.each { |element| puts element } # good collection.each do |element| puts element end # If the AllowBracesOnProceduralOneLiners option is set to `true`, or # any other truthy value, then one-line procedural blocks may use # either style. (There is no setting for requiring braces on them.) # good collection.each { |element| puts element } # also good collection.each do |element| puts element end
@example EnforcedStyle: braces_for_chaining
# bad words.each do |word| word.flip.flop end.join("-") # good words.each { |word| word.flip.flop }.join("-")
@example EnforcedStyle: always_braces
# bad words.each do |word| word.flip.flop end # good words.each { |word| word.flip.flop }
@example BracesRequiredMethods: [‘sig’]
# Methods listed in the BracesRequiredMethods list, such as 'sig' # in this example, will require `{...}` braces. This option takes # precedence over all other configurations except AllowedMethods. # bad sig do params( foo: string, ).void end def bar(foo) puts foo end # good sig { params( foo: string, ).void } def bar(foo) puts foo end
@example AllowedMethods: [‘lambda’, ‘proc’, ‘it’ ] (default)
# good foo = lambda do |x| puts "Hello, #{x}" end foo = lambda do |x| x * 100 end
@example AllowedPatterns: [] (default)
# bad things.map { |thing| something = thing.some_method process(something) }
@example AllowedPatterns: [‘map’]
# good things.map { |thing| something = thing.some_method process(something) }
Constants
- ALWAYS_BRACES_MESSAGE
- BRACES_REQUIRED_MESSAGE
Public Class Methods
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 179 def self.autocorrect_incompatible_with [Style::RedundantBegin] end
Public Instance Methods
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 200 def on_block(node) return if ignored_node?(node) return if proper_block_style?(node) message = message(node) add_offense(node.loc.begin, message: message) do |corrector| autocorrect(corrector, node) end end
Also aliased as: on_numblock, on_itblock
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 183 def on_send(node) return unless node.arguments? return if node.parenthesized? return if node.assignment_method? return if single_argument_operator_method?(node) node.arguments.each do |arg| get_blocks(arg) do |block| # If there are no parentheses around the arguments, then braces # and do-end have different meaning due to how they bind, so we # allow either. ignore_node(block) end end end
Also aliased as: on_csend
Private Instance Methods
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 484 def array_or_range?(node) node.type?(:array, :range) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 215 def autocorrect(corrector, node) return if correction_would_break_code?(node) if node.braces? replace_braces_with_do_end(corrector, node.loc) else replace_do_end_with_braces(corrector, node) end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 488 def begin_required?(block_node) # If the block contains `rescue` or `ensure`, it needs to be wrapped in # `begin`...`end` when changing `do-end` to `{}`. block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line? end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 243 def braces_for_chaining_message(node) if node.multiline? if node.chained? 'Prefer `{...}` over `do...end` for multi-line chained blocks.' else 'Prefer `do...end` for multi-line blocks without chaining.' end else 'Prefer `{...}` over `do...end` for single-line blocks.' end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 429 def braces_for_chaining_style?(node) block_begin = node.loc.begin.source block_begin == if node.multiline? (node.chained? ? '{' : 'do') else '{' end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 255 def braces_required_message(node) format(BRACES_REQUIRED_MESSAGE, method_name: node.method_name.to_s) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 406 def braces_required_method?(method_name) braces_required_methods.include?(method_name.to_s) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 410 def braces_required_methods cop_config.fetch('BracesRequiredMethods', []) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 439 def braces_style?(node) node.loc.begin.source == '{' end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 443 def correction_would_break_code?(node) return false unless node.keywords? node.send_node.arguments? && !node.send_node.parenthesized? end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 331 def end_of_chain(node) return end_of_chain(node.block_node) if with_block?(node) return node unless node.chained? end_of_chain(node.parent) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 453 def functional_block?(node) return_value_used?(node) || return_value_of_scope?(node) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 449 def functional_method?(method_name) cop_config['FunctionalMethods'].map(&:to_sym).include?(method_name) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 349 def get_blocks(node, &block) case node.type when :block, :numblock, :itblock yield node when :send, :csend # When a method has an argument which is another method with a block, # that block needs braces, otherwise a syntax error will be introduced # for subsequent arguments. # Additionally, even without additional arguments, changing `{...}` to # `do...end` will change the binding of the block to the outer method. get_blocks(node.receiver, &block) if node.receiver node.arguments.each { |argument| get_blocks(argument, &block) } when :hash # A hash which is passed as method argument may have no braces # In that case, one of the K/V pairs could contain a block node # which could change in meaning if `do...end` is replaced with `{...}` return if node.braces? node.each_child_node { |child| get_blocks(child, &block) } when :pair node.each_child_node { |child| get_blocks(child, &block) } end end
rubocop:disable Metrics/CyclomaticComplexity
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 414 def line_count_based_block_style?(node) node.multiline? ^ node.braces? end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 225 def line_count_based_message(node) if node.multiline? 'Avoid using `{...}` for multi-line blocks.' else 'Prefer `{...}` over `do...end` for single-line blocks.' end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 259 def message(node) return braces_required_message(node) if braces_required_method?(node.method_name) case style when :line_count_based then line_count_based_message(node) when :semantic then semantic_message(node) when :braces_for_chaining then braces_for_chaining_message(node) when :always_braces then ALWAYS_BRACES_MESSAGE end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 307 def move_comment_before_block(corrector, comment, block_node, closing_brace) range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace # It is possible that there is code between the block and the comment # which needs to be preserved and trimmed. pre_comment_range = source_range_before_comment(range, comment) corrector.remove(range_with_surrounding_space(comment.source_range, side: :right)) remove_trailing_whitespace(corrector, pre_comment_range, comment) corrector.insert_after(pre_comment_range, "\n") corrector.insert_before(block_node, "#{comment.text}\n") end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 461 def procedural_method?(method_name) cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 457 def procedural_oneliners_may_have_braces? cop_config['AllowBracesOnProceduralOneLiners'] end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 374 def proper_block_style?(node) return true if require_do_end?(node) return special_method_proper_block_style?(node) if special_method?(node.method_name) case style when :line_count_based then line_count_based_block_style?(node) when :semantic then semantic_block_style?(node) when :braces_for_chaining then braces_for_chaining_style?(node) when :always_braces then braces_style?(node) end end
rubocop:enable Metrics/CyclomaticComplexity
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 338 def remove_trailing_whitespace(corrector, range, comment) range_of_trailing = range.end.join(comment.source_range.begin) corrector.remove(range_of_trailing) if range_of_trailing.source.match?(/\A\s+\z/) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 270 def replace_braces_with_do_end(corrector, loc) b = loc.begin e = loc.end corrector.insert_before(b, ' ') unless whitespace_before?(b) corrector.insert_before(e, ' ') unless whitespace_before?(e) corrector.insert_after(b, ' ') unless whitespace_after?(b) corrector.replace(b, 'do') if (comment = processed_source.comment_at_line(e.line)) move_comment_before_block(corrector, comment, loc.node, e) end corrector.replace(e, 'end') end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 286 def replace_do_end_with_braces(corrector, node) loc = node.loc b = loc.begin e = loc.end corrector.insert_after(b, ' ') unless whitespace_after?(b, 2) corrector.replace(b, '{') corrector.replace(e, '}') corrector.wrap(node.body, "begin\n", "\nend") if begin_required?(node) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 386 def require_do_end?(node) return false if node.braces? || node.multiline? return false unless (resbody = node.each_descendant(:resbody).first) resbody.children.first&.array_type? end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 477 def return_value_of_scope?(node) return false unless (parent = node.parent) parent.conditional? || parent.operator_keyword? || array_or_range?(parent) || parent.children.last == node end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 465 def return_value_used?(node) return false unless node.parent # If there are parentheses around the block, check if that # is being used. if node.parent.begin_type? return_value_used?(node.parent) else node.parent.assignment? || node.parent.call_type? end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 418 def semantic_block_style?(node) method_name = node.method_name if node.braces? functional_method?(method_name) || functional_block?(node) || (procedural_oneliners_may_have_braces? && !node.multiline?) else procedural_method?(method_name) || !return_value_used?(node) end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 233 def semantic_message(node) block_begin = node.loc.begin.source if block_begin == '{' 'Prefer `do...end` over `{...}` for procedural blocks.' else 'Prefer `{...}` over `do...end` for functional blocks.' end end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 494 def single_argument_operator_method?(node) return false unless node.operator_method? node.arguments.one? && node.first_argument.block_type? end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 321 def source_range_before_comment(range, comment) range = range.end.join(comment.source_range.begin) # End the range before any whitespace that precedes the comment trailing_whitespace_count = range.source[/\s+\z/]&.length range = range.adjust(end_pos: -trailing_whitespace_count) if trailing_whitespace_count range end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 393 def special_method?(method_name) allowed_method?(method_name) || matches_allowed_pattern?(method_name) || braces_required_method?(method_name) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 399 def special_method_proper_block_style?(node) method_name = node.method_name return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name) node.braces? if braces_required_method?(method_name) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 303 def whitespace_after?(range, length = 1) /\s/.match?(range.source_buffer.source[range.begin_pos + length, 1]) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 299 def whitespace_before?(range) /\s/.match?(range.source_buffer.source[range.begin_pos - 1, 1]) end
Source
# File lib/rubocop/cop/style/block_delimiters.rb, line 344 def with_block?(node) node.respond_to?(:block_node) && node.block_node end