# encoding: utf-8# frozen_string_literal: truemoduleRuboCop# 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.classCommentConfigUNNEEDED_DISABLE='Lint/UnneededDisable'.freezeCOP_NAME_PATTERN='([A-Z][a-z]+/)?(?:[A-Z][a-z]+)+'.freezeCOP_NAMES_PATTERN="(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}".freezeCOPS_PATTERN="(all|#{COP_NAMES_PATTERN})".freezeCOMMENT_DIRECTIVE_REGEXP=Regexp.new(('\A# rubocop : ((?:dis|en)able)\b '+COPS_PATTERN).gsub(' ','\s*'))CopAnalysis=Struct.new(:line_ranges,:start_line_number)attr_reader:processed_sourcedefinitialize(processed_source)@processed_source=processed_sourceenddefcop_enabled_at_line?(cop,line_number)cop=cop.cop_nameifcop.respond_to?(:cop_name)disabled_line_ranges=cop_disabled_line_ranges[cop]returntrueunlessdisabled_line_rangesdisabled_line_ranges.none?{|range|range.include?(line_number)}enddefcop_disabled_line_ranges@cop_disabled_line_ranges||=analyzeendprivatedefanalyzeanalyses=Hash.new{|hash,key|hash[key]=CopAnalysis.new([],nil)}each_mentioned_copdo|cop_name,disabled,line,single_line|analyses[cop_name]=analyze_cop(analyses[cop_name],disabled,line,single_line)endanalyses.each_with_object({})do|element,hash|cop_name,analysis=*elementhash[cop_name]=cop_line_ranges(analysis)endenddefanalyze_cop(analysis,disabled,line,single_line)ifsingle_lineanalyze_single_line(analysis,line,disabled)elsifdisabledanalyze_disabled(analysis,line)elseanalyze_rest(analysis,line)endenddefanalyze_single_line(analysis,line,disabled)returnanalysisunlessdisabledCopAnalysis.new(analysis.line_ranges+[(line..line)],analysis.start_line_number)enddefanalyze_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.returnCopAnalysis.new(analysis.line_ranges+[start_line..line],line)endCopAnalysis.new(analysis.line_ranges,line)enddefanalyze_rest(analysis,line)if(start_line=analysis.start_line_number)returnCopAnalysis.new(analysis.line_ranges+[start_line..line],nil)endCopAnalysis.new(analysis.line_ranges,nil)enddefcop_line_ranges(analysis)returnanalysis.line_rangesunlessanalysis.start_line_numberanalysis.line_ranges+[(analysis.start_line_number..Float::INFINITY)]enddefeach_mentioned_copeach_directivedo|comment,cop_names,disabled|comment_line_number=comment.loc.expression.linesingle_line=!comment_only_line?(comment_line_number)cop_names.eachdo|cop_name|yieldqualified_cop_name(cop_name),disabled,comment_line_number,single_lineendendenddefeach_directivereturnifprocessed_source.comments.nil?processed_source.comments.eachdo|comment|directive=directive_parts(comment)nextunlessdirectiveyieldcomment,*directiveendenddefdirective_parts(comment)match=comment.text.match(COMMENT_DIRECTIVE_REGEXP)returnunlessmatchswitch,cops_string=match.capturescop_names=cops_string=='all'?all_cop_names:cops_string.split(/,\s*/)disabled=(switch=='disable')[cop_names,disabled]enddefqualified_cop_name(cop_name)Cop::Cop.qualified_cop_name(cop_name.strip,processed_source.buffer.name)enddefall_cop_names@all_cop_names||=Cop::Cop.all.map(&:cop_name).rejectdo|cop_name|cop_name==UNNEEDED_DISABLEendenddefcomment_only_line?(line_number)non_comment_token_line_numbers.none?do|non_comment_line_number|non_comment_line_number==line_numberendenddefnon_comment_token_line_numbers@non_comment_token_line_numbers||=beginnon_comment_tokens=processed_source.tokens.rejectdo|token|token.type==:tCOMMENTendnon_comment_tokens.map{|token|token.pos.line}.uniqendendendend