lib/rubocop/comment_config.rb in rubocop-0.40.0 vs lib/rubocop/comment_config.rb in rubocop-0.41.0

- old
+ new

@@ -4,81 +4,134 @@ module RuboCop # This class parses the special `rubocop:disable` comments in a source # and provides a way to check if each cop is enabled at arbitrary line. class CommentConfig UNNEEDED_DISABLE = 'Lint/UnneededDisable'.freeze + + COP_NAME_PATTERN = '([A-Z][a-z]+/)?(?:[A-Z][a-z]+)+'.freeze + COP_NAMES_PATTERN = "(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}".freeze + COPS_PATTERN = "(all|#{COP_NAMES_PATTERN})".freeze + COMMENT_DIRECTIVE_REGEXP = Regexp.new( - '\A# rubocop : ((?:dis|en)able)\b ((?:[\w/]+,? )+)'.gsub(' ', '\s*') + ('\A# rubocop : ((?:dis|en)able)\b ' + COPS_PATTERN).gsub(' ', '\s*') ) + CopAnalysis = Struct.new(:line_ranges, :start_line_number) + attr_reader :processed_source def initialize(processed_source) @processed_source = processed_source end def cop_enabled_at_line?(cop, line_number) cop = cop.cop_name if cop.respond_to?(:cop_name) disabled_line_ranges = cop_disabled_line_ranges[cop] + return true unless disabled_line_ranges + disabled_line_ranges.none? { |range| range.include?(line_number) } end def cop_disabled_line_ranges @cop_disabled_line_ranges ||= analyze end private def analyze - disabled_line_ranges = Hash.new { |hash, key| hash[key] = [] } - disablement_start_line_numbers = {} + analyses = Hash.new { |hash, key| hash[key] = CopAnalysis.new([], nil) } each_mentioned_cop do |cop_name, disabled, line, single_line| - if single_line - disabled_line_ranges[cop_name] << (line..line) if disabled - elsif disabled - if disablement_start_line_numbers[cop_name] - # Cop already disabled on this line, so we end the current disabled - # range before we start a new range. - start_line = disablement_start_line_numbers.delete(cop_name) - disabled_line_ranges[cop_name] << (start_line..line) - end - disablement_start_line_numbers[cop_name] = line - else - start_line = disablement_start_line_numbers.delete(cop_name) - disabled_line_ranges[cop_name] << (start_line..line) if start_line - end + analyses[cop_name] = + analyze_cop(analyses[cop_name], disabled, line, single_line) end - disablement_start_line_numbers.each do |cop_name, start_line| - disabled_line_ranges[cop_name] << (start_line..Float::INFINITY) + analyses.each_with_object({}) do |element, hash| + cop_name, analysis = *element + hash[cop_name] = cop_line_ranges(analysis) end + end - disabled_line_ranges + def analyze_cop(analysis, disabled, line, single_line) + if single_line + analyze_single_line(analysis, line, disabled) + elsif disabled + analyze_disabled(analysis, line) + else + analyze_rest(analysis, line) + end end - def each_mentioned_cop - return if processed_source.comments.nil? + def analyze_single_line(analysis, line, disabled) + return analysis unless disabled - processed_source.comments.each do |comment| - match = comment.text.match(COMMENT_DIRECTIVE_REGEXP) - next unless match + CopAnalysis.new(analysis.line_ranges + [(line..line)], + analysis.start_line_number) + end - switch, cops_string = match.captures + def analyze_disabled(analysis, line) + if (start_line = analysis.start_line_number) + # Cop already disabled on this line, so we end the current disabled + # range before we start a new range. + return CopAnalysis.new(analysis.line_ranges + [start_line..line], line) + end - cop_names = - cops_string == 'all' ? all_cop_names : cops_string.split(/,\s*/) + CopAnalysis.new(analysis.line_ranges, line) + end - disabled = (switch == 'disable') + def analyze_rest(analysis, line) + if (start_line = analysis.start_line_number) + return CopAnalysis.new(analysis.line_ranges + [start_line..line], nil) + end + + CopAnalysis.new(analysis.line_ranges, nil) + end + + def cop_line_ranges(analysis) + return analysis.line_ranges unless analysis.start_line_number + + analysis.line_ranges + [(analysis.start_line_number..Float::INFINITY)] + end + + def each_mentioned_cop + each_directive do |comment, cop_names, disabled| comment_line_number = comment.loc.expression.line single_line = !comment_only_line?(comment_line_number) cop_names.each do |cop_name| - cop_name = Cop::Cop.qualified_cop_name(cop_name.strip, - processed_source.buffer.name) - yield cop_name, disabled, comment_line_number, single_line + yield qualified_cop_name(cop_name), disabled, comment_line_number, + single_line end end + end + + def each_directive + return if processed_source.comments.nil? + + processed_source.comments.each do |comment| + directive = directive_parts(comment) + next unless directive + + yield comment, *directive + end + end + + def directive_parts(comment) + match = comment.text.match(COMMENT_DIRECTIVE_REGEXP) + return unless match + + switch, cops_string = match.captures + + cop_names = + cops_string == 'all' ? all_cop_names : cops_string.split(/,\s*/) + + disabled = (switch == 'disable') + + [cop_names, disabled] + end + + def qualified_cop_name(cop_name) + Cop::Cop.qualified_cop_name(cop_name.strip, processed_source.buffer.name) end def all_cop_names @all_cop_names ||= Cop::Cop.all.map(&:cop_name).reject do |cop_name| cop_name == UNNEEDED_DISABLE