# frozen_string_literal: truemoduleRuboCopmoduleCop# Error raised when an unqualified cop name is used that could# refer to two or more cops under different departmentsclassAmbiguousCopName<RuboCop::ErrorMSG='Ambiguous cop name `%<name>s` used in %<origin>s needs '\'department qualifier. Did you mean %<options>s?'definitialize(name,origin,badges)super(format(MSG,name: name,origin: origin,options: badges.to_a.join(' or ')))endend# Registry that tracks all cops by their badge and department.classRegistryincludeEnumerabledefself.allglobal.without_department(:Test).copsenddefself.qualified_cop_name(name,origin,warn: true)global.qualified_cop_name(name,origin,warn: warn)end# Changes momentarily the global registry# Intended for testing purposesdefself.with_temporary_global(temp_global=global.dup)previous=@global@global=temp_globalyieldensure@global=previousenddefself.reset!@global=newenddefself.qualified_cop?(name)badge=Badge.parse(name)global.qualify_badge(badge).first==badgeendattr_reader:optionsdefinitialize(cops=[],options={})@registry={}@departments={}@cops_by_cop_name=Hash.new{|hash,key|hash[key]=[]}@enrollment_queue=cops@options=options@enabled_cache={}.compare_by_identity@disabled_cache={}.compare_by_identityenddefenlist(cop)@enrollment_queue<<copenddefdismiss(cop)raise"Cop #{cop} could not be dismissed"unless@enrollment_queue.delete(cop)end# @return [Array<Symbol>] list of departments for current cops.defdepartmentsclear_enrollment_queue@departments.keysend# @return [Registry] Cops for that specific department.defwith_department(department)clear_enrollment_queuewith(@departments.fetch(department,[]))end# @return [Registry] Cops not for a specific department.defwithout_department(department)clear_enrollment_queuewithout_department=@departments.dupwithout_department.delete(department)with(without_department.values.flatten)end# @return [Boolean] Checks if given name is departmentdefdepartment?(name)departments.include?(name.to_sym)enddefcontains_cop_matching?(names)cops.any?{|cop|cop.match?(names)}end# Convert a user provided cop name into a properly namespaced name## @example gives back a correctly qualified cop name## registry = RuboCop::Cop::Registry# registry.qualified_cop_name('Layout/EndOfLine', '') # => 'Layout/EndOfLine'## @example fixes incorrect namespaces## registry = RuboCop::Cop::Registry# registry.qualified_cop_name('Lint/EndOfLine', '') # => 'Layout/EndOfLine'## @example namespaces bare cop identifiers## registry = RuboCop::Cop::Registry# registry.qualified_cop_name('EndOfLine', '') # => 'Layout/EndOfLine'## @example passes back unrecognized cop names## registry = RuboCop::Cop::Registry# registry.qualified_cop_name('NotACop', '') # => 'NotACop'## @param name [String] Cop name extracted from config# @param path [String, nil] Path of file that `name` was extracted from# @param warn [Boolean] Print a warning if no department given for `name`## @raise [AmbiguousCopName]# if a bare identifier with two possible namespaces is provided## @note Emits a warning if the provided name has an incorrect namespace## @return [String] Qualified cop namedefqualified_cop_name(name,path,warn: true)badge=Badge.parse(name)print_warning(name,path)ifwarn&&department_missing?(badge,name)returnnameifregistered?(badge)potential_badges=qualify_badge(badge)casepotential_badges.sizewhen0thenname# No namespace found. Deal with it later in caller.when1thenresolve_badge(badge,potential_badges.first,path,warn: warn)elseraiseAmbiguousCopName.new(badge,path,potential_badges)endenddefdepartment_missing?(badge,name)!badge.qualified?&&unqualified_cop_names.include?(name)enddefprint_warning(name,path)message="#{path}: Warning: no department given for #{name}."ifpath.end_with?('.rb')message+=' Run `rubocop -a --only Migration/DepartmentName` to fix.'endwarnmessageenddefunqualified_cop_namesclear_enrollment_queue@unqualified_cop_names||=Set.new(@cops_by_cop_name.keys.map{|qn|File.basename(qn)})<<'RedundantCopDisableDirective'enddefqualify_badge(badge)clear_enrollment_queue@departments.map{|department,_|badge.with_department(department)}.select{|potential_badge|registered?(potential_badge)}end# @return [Hash{String => Array<Class>}]defto_hclear_enrollment_queue@cops_by_cop_nameenddefcopsclear_enrollment_queue@registry.valuesenddeflengthclear_enrollment_queue@registry.sizeenddefenabled(config)@enabled_cache[config]||=select{|cop|enabled?(cop,config)}enddefdisabled(config)@disabled_cache[config]||=reject{|cop|enabled?(cop,config)}enddefenabled?(cop,config)returntrueifoptions[:only]&.include?(cop.cop_name)# We need to use `cop_name` in this case, because `for_cop` uses caching# which expects cop names or cop classes as keys.cfg=config.for_cop(cop.cop_name)cop_enabled=cfg.fetch('Enabled')==true||enabled_pending_cop?(cfg,config)ifoptions.fetch(:safe,false)cop_enabled&&cfg.fetch('Safe',true)elsecop_enabledendenddefenabled_pending_cop?(cop_cfg,config)returnfalseif@options[:disable_pending_cops]cop_cfg.fetch('Enabled')=='pending'&&(@options[:enable_pending_cops]||config.enabled_new_cops?)enddefnamescops.map(&:cop_name)enddefcops_for_department(department)cops.select{|cop|cop.department==department.to_sym}enddefnames_for_department(department)cops_for_department(department).map(&:cop_name)enddef==(other)cops==other.copsenddefsort!clear_enrollment_queue@registry=@registry.sort_by{|badge,_|badge.cop_name}.to_hselfenddefselect(&block)cops.select(&block)enddefeach(&block)cops.each(&block)end# @param [String] cop_name# @return [Class, nil]deffind_by_cop_name(cop_name)to_h[cop_name].firstend# When a cop name is given returns a single-element array with the cop class.# When a department name is given returns an array with all the cop classes# for that department.deffind_cops_by_directive(directive)cop=find_by_cop_name(directive)cop?[cop]:cops_for_department(directive)enddeffreezeclear_enrollment_queueunqualified_cop_names# build cachesuperend@global=newclass<<selfattr_reader:globalendprivatedefinitialize_copy(reg)initialize(reg.cops,reg.options)enddefclear_enrollment_queuereturnif@enrollment_queue.empty?@enrollment_queue.eachdo|cop|@registry[cop.badge]=cop@departments[cop.department]||=[]@departments[cop.department]<<cop@cops_by_cop_name[cop.cop_name]<<copend@enrollment_queue=[]enddefwith(cops)self.class.new(cops)enddefresolve_badge(given_badge,real_badge,source_path,warn: true)unlessgiven_badge.match?(real_badge)path=PathUtil.smart_path(source_path)ifwarnwarn("#{path}: #{given_badge} has the wrong namespace - "\"replace it with #{given_badge.with_department(real_badge.department)}")endendreal_badge.to_senddefregistered?(badge)clear_enrollment_queue@registry.key?(badge)endendendend