# frozen_string_literal: truerequire'uri'moduleRuboCopmoduleCopclassAmbiguousCopName<RuboCop::Error;end# Store for all cops with helper functionsclassCopStore<::Array# @return [Array<String>] list of types for current cops.deftypes@types||=map(&:cop_type).uniq!end# @return [Array<Cop>] Cops for that specific type.defwith_type(type)CopStore.new(select{|c|c.cop_type==type})end# @return [Array<Cop>] Cops not for a specific type.defwithout_type(type)CopStore.new(reject{|c|c.cop_type==type})enddefqualified_cop_name(name,origin)returnnameifcop_names.include?(name)basename=File.basename(name)found_ns=types.map(&:capitalize).selectdo|ns|cop_names.include?("#{ns}/#{basename}")endcasefound_ns.sizewhen0thenname# No namespace found. Deal with it later in caller.when1thencop_name_with_namespace(name,origin,basename,found_ns[0])elseraiseAmbiguousCopName,"Ambiguous cop name `#{name}` used in #{origin} needs "\'namespace qualifier. Did you mean '\"#{found_ns.map{|ns|"#{ns}/#{basename}"}.join(' or ')}"endenddefcop_name_with_namespace(name,origin,basename,found_ns)ifname!=basename&&found_ns!=File.dirname(name).to_symwarn"#{origin}: #{name} has the wrong namespace - should be "\"#{found_ns}"end"#{found_ns}/#{basename}"endprivatedefcop_names@cop_names||=Set.new(map(&:cop_name))endend# A scaffold for concrete cops.## The Cop class is meant to be extended.## Cops track offenses and can autocorrect them on the fly.## A commissioner object is responsible for traversing the AST and invoking# the specific callbacks on each cop.# If a cop needs to do its own processing of the AST or depends on# something else, it should define the `#investigate` method and do# the processing there.## @example## class CustomCop < Cop# def investigate(processed_source)# # Do custom processing# end# endclassCopextendRuboCop::SexpextendNodePattern::MacrosincludeRuboCop::SexpincludeUtilincludeIgnoredNodeincludeAutocorrectLogicattr_reader:config,:offenses,:correctionsattr_accessor:processed_source# TODO: Bad design.@all=CopStore.newdefself.all@all.without_type(:test)enddefself.qualified_cop_name(name,origin)@all.qualified_cop_name(name,origin)enddefself.non_railsall.without_type(:rails)enddefself.inherited(subclass)@all<<subclassenddefself.cop_name@cop_name||=name.split('::').last(2).join('/')enddefself.cop_type@cop_type||=name.split('::')[-2].downcase.to_symenddefself.lint?cop_type==:lintend# Returns true if the cop name or the cop namespace matches any of the# given names.defself.match?(given_names)returnfalseunlessgiven_namesgiven_names.include?(cop_name)||given_names.include?(cop_type.to_s.capitalize)enddefinitialize(config=nil,options=nil)@config=config||Config.new@options=options||{debug: false}@offenses=[]@corrections=[]enddefjoin_force?(_force_class)falseenddefcop_config@cop_config||=@config.for_cop(self)enddefdebug?@options[:debug]enddefdisplay_cop_names?debug?||@options[:display_cop_names]||@config.for_all_cops['DisplayCopNames']enddefdisplay_style_guide?(style_guide_url||reference_url)&&(@options[:display_style_guide]||config.for_all_cops['DisplayStyleGuide'])enddefextra_details?@options[:extra_details]||config.for_all_cops['ExtraDetails']enddefmessage(_node=nil)self.class::MSGenddefadd_offense(node,loc,message=nil,severity=nil)location=find_location(node,loc)returnifduplicate_location?(location)severity=custom_severity||severity||default_severitymessage||=message(node)message=annotate_message(message)status=enabled_line?(location.line)?correct(node)::disabled@offenses<<Offense.new(severity,location,message,name,status)yieldifblock_given?&&status!=:disabledenddeffind_location(node,loc)# Location can be provided as a symbol, e.g.: `:keyword`loc.is_a?(Symbol)?node.loc.public_send(loc):locenddefduplicate_location?(location)@offenses.any?{|o|o.location==location}enddefcorrect(node)return:unsupportedunlesssupport_autocorrect?return:uncorrectedunlessautocorrect?correction=autocorrect(node)return:uncorrectedunlesscorrection@corrections<<correction:correctedenddefconfig_to_allow_offensesFormatter::DisabledConfigFormatter.config_to_allow_offenses[cop_name]||={}enddefconfig_to_allow_offenses=(hash)Formatter::DisabledConfigFormatter.config_to_allow_offenses[cop_name]=hashenddeftarget_ruby_version@config.target_ruby_versionenddefparse(source,path=nil)ProcessedSource.new(source,target_ruby_version,path)enddefcop_name@cop_name||=self.class.cop_nameendaliasnamecop_namedefrelevant_file?(file)file_name_matches_any?(file,'Include',true)&&!file_name_matches_any?(file,'Exclude',false)enddefexcluded_file?(file)!relevant_file?(file)enddefstyle_guide_urlurl=cop_config['StyleGuide']returnnilifurl.nil?||url.empty?base_url=config.for_all_cops['StyleGuideBaseURL']returnurlifbase_url.nil?||base_url.empty?URI.join(base_url,url).to_senddefreference_urlurl=cop_config['Reference']url.nil?||url.empty??nil:urlenddefdetailsdetails=cop_config&&cop_config['Details']details.nil?||details.empty??nil:detailsendprivatedefannotate_message(message)message="#{name}: #{message}"ifdisplay_cop_names?message+=" #{details}"ifextra_details?ifdisplay_style_guide?links=[style_guide_url,reference_url].compact.join(', ')message="#{message} (#{links})"endmessageenddeffile_name_matches_any?(file,parameter,default_result)patterns=cop_config[parameter]returndefault_resultunlesspatternspath=nilpatterns.any?do|pattern|# Try to match the absolute path, as Exclude properties are absolute.nexttrueifmatch_path?(pattern,file)# Try with relative path.path||=config.path_relative_to_config(file)match_path?(pattern,path)endenddefenabled_line?(line_number)returntrueunless@processed_source@processed_source.comment_config.cop_enabled_at_line?(self,line_number)enddefdefault_severityself.class.lint??:warning::conventionenddefcustom_severityseverity=cop_config['Severity']returnunlessseverityifSeverity::NAMES.include?(severity.to_sym)severity.to_symelsemessage="Warning: Invalid severity '#{severity}'. "\"Valid severities are #{Severity::NAMES.join(', ')}."warn(Rainbow(message).red)endendendendend