# frozen_string_literal: truerequire'uri'moduleRuboCopmoduleCop# 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::AST::SexpextendNodePattern::MacrosincludeRuboCop::AST::SexpincludeUtilincludeIgnoredNodeincludeAutocorrectLogicCorrection=Struct.new(:lambda,:node,:cop)dodefcall(corrector)lambda.call(corrector)rescueStandardError=>eraiseErrorWithAnalyzedFileLocation.new(cause: e,node: node,cop: cop)endendattr_reader:config,:offenses,:correctionsattr_accessor:processed_source# TODO: Bad design.@registry=Registry.newclass<<selfattr_reader:registryenddefself.allregistry.without_department(:Test).copsenddefself.qualified_cop_name(name,origin)registry.qualified_cop_name(name,origin)enddefself.inherited(subclass)registry.enlist(subclass)enddefself.badge@badge||=Badge.for(name)enddefself.cop_namebadge.to_senddefself.departmentbadge.departmentenddefself.lint?department==: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?(department.to_s)end# List of cops that should not try to autocorrect at the same# time as this cop## @return [Array<RuboCop::Cop::Cop>]## @api publicdefself.autocorrect_incompatible_with[]enddefinitialize(config=nil,options=nil)@config=config||Config.new@options=options||{debug: false}@offenses=[]@corrections=[]@corrected_nodes={}@corrected_nodes.compare_by_identity@processed_source=nilenddefjoin_force?(_force_class)falseenddefcop_config# Use department configuration as basis, but let individual cop# configuration override.@cop_config||=@config.for_cop(self.class.department.to_s).merge(@config.for_cop(self))enddefmessage(_node=nil)self.class::MSGenddefadd_offense(node,location: :expression,message: nil,severity: nil)loc=find_location(node,location)returnifduplicate_location?(loc)severity=find_severity(node,severity)message=find_message(node,message)status=enabled_line?(loc.line)?correct(node)::disabled@offenses<<Offense.new(severity,loc,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)reason=reason_to_not_correct(node)returnreasonifreason@corrected_nodes[node]=trueifsupport_autocorrect?correction=autocorrect(node)return:uncorrectedunlesscorrection@corrections<<Correction.new(correction,node,self):correctedelsifdisable_uncorrectable?disable_uncorrectable(node):corrected_with_todoendenddefreason_to_not_correct(node)return:unsupportedunlesscorrectable?return:uncorrectedunlessautocorrect?return:already_correctedif@corrected_nodes.key?(node)nilenddefdisable_uncorrectable(node)@disabled_lines||={}line=node.location.linereturnif@disabled_lines.key?(line)@disabled_lines[line]=true@corrections<<Correction.new(disable_offense(node),node,self)enddefconfig_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_versionenddeftarget_rails_version@config.target_rails_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)endprivatedeffind_message(node,message)annotate(message||message(node))enddefannotate(message)RuboCop::Cop::MessageAnnotator.new(config,cop_name,cop_config,@options).annotate(message)enddeffile_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)returntrueif@options[:ignore_disable_comments]||!@processed_source@processed_source.comment_config.cop_enabled_at_line?(self,line_number)enddeffind_severity(_node,severity)custom_severity||severity||default_severityenddefdefault_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