class RuboCop::Cop::InternalAffairs::NodeMatcherDirective
Checks that node matcher definitions are tagged with a YARD `@!method` directive so that editors are able to find the dynamically defined method.
@example
# bad def_node_matcher :foo?, <<~PATTERN ... PATTERN # good # @!method foo?(node) def_node_matcher :foo?, <<~PATTERN ... PATTERN
Constants
- MSG
- MSG_TOO_MANY
- MSG_WRONG_NAME
- REGEXP
- RESTRICT_ON_SEND
Public Instance Methods
on_send(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 39 def on_send(node) return if node.arguments.none? return unless valid_method_name?(node) actual_name = node.arguments.first.value directives = method_directives(node) return too_many_directives(node) if directives.size > 1 directive = directives.first return if directive_correct?(directive, actual_name) register_offense(node, directive, actual_name) end
Private Instance Methods
add_newline?(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 117 def add_newline?(node) # Determine if a blank line should be inserted before the new directive # in order to spread out pattern matchers return if node.sibling_index&.zero? return unless node.parent prev_sibling = node.parent.child_nodes[node.sibling_index - 1] return unless prev_sibling && pattern_matcher?(prev_sibling) node.loc.line == last_line(prev_sibling) + 1 end
correct_directive(corrector, directive, actual_name)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 137 def correct_directive(corrector, directive, actual_name) correct = "@!method #{actual_name}" regexp = /@!method\s+#{Regexp.escape(directive[:method_name])}/ replacement = directive[:node].text.gsub(regexp, correct) corrector.replace(directive[:node], replacement) end
directive_correct?(directive, actual_name)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 74 def directive_correct?(directive, actual_name) directive && directive[:method_name] == actual_name.to_s end
formatted_message(directive, actual_name, method_name)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 90 def formatted_message(directive, actual_name, method_name) if directive format(MSG_WRONG_NAME, expected: actual_name, actual: directive[:method_name]) else format(MSG, method: method_name) end end
insert_directive(corrector, node, actual_name)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 98 def insert_directive(corrector, node, actual_name) # If the pattern matcher uses arguments (`%1`, `%2`, etc.), include them in the directive arguments = pattern_arguments(node.arguments[1].source) range = range_with_surrounding_space(node.loc.expression, side: :left, newlines: false) indentation = range.source.match(/^\s*/)[0] directive = "#{indentation}# @!method #{actual_name}(#{arguments.join(', ')})\n" directive = "\n#{directive}" if add_newline?(node) corrector.insert_before(range, directive) end
last_line(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 129 def last_line(node) if node.last_argument.heredoc? node.last_argument.loc.heredoc_end.line else node.loc.last_line end end
method_directives(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 59 def method_directives(node) comments = processed_source.ast_with_comments[node] comments.map do |comment| match = comment.text.match(REGEXP) next unless match { node: comment, method_name: match[:method_name], args: match[:args] } end.compact end
pattern_arguments(pattern)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 110 def pattern_arguments(pattern) arguments = %w[node] max_pattern_var = pattern.scan(/(?<=%)\d+/).map(&:to_i).max max_pattern_var&.times { |i| arguments << "arg#{i + 1}" } arguments end
register_offense(node, directive, actual_name)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 78 def register_offense(node, directive, actual_name) message = formatted_message(directive, actual_name, node.method_name) add_offense(node, message: message) do |corrector| if directive correct_directive(corrector, directive, actual_name) else insert_directive(corrector, node, actual_name) end end end
too_many_directives(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 70 def too_many_directives(node) add_offense(node, message: MSG_TOO_MANY) end
valid_method_name?(node)
click to toggle source
# File lib/rubocop/cop/internal_affairs/node_matcher_directive.rb, line 55 def valid_method_name?(node) node.arguments.first.str_type? || node.arguments.first.sym_type? end