# frozen_string_literal: truemoduleRuboCopmoduleCop# Commissioner class is responsible for processing the AST and delegating# work to the specified cops.classCommissionerincludeRuboCop::AST::TraversalRESTRICTED_CALLBACKS=%i[on_send on_csend after_send after_csend].freezeprivate_constant:RESTRICTED_CALLBACKS# How a Commissioner returns the results of the investigation# as a list of Cop::InvestigationReport and any errors caught# during the investigation.# Immutable# Consider creation API privateInvestigationReport=Struct.new(:processed_source,:cop_reports,:errors)dodefcops@cops||=cop_reports.map(&:cop)enddefoffenses_per_cop@offenses_per_cop||=cop_reports.map(&:offenses)enddefcorrectors@correctors||=cop_reports.map(&:corrector)enddefoffenses@offenses||=offenses_per_cop.flatten(1)enddefmerge(investigation)InvestigationReport.new(processed_source,cop_reports+investigation.cop_reports,errors+investigation.errors)endendattr_reader:errorsdefinitialize(cops,forces=[],options={})@cops=cops@forces=forces@options=optionsinitialize_callbacksresetend# Create methods like :on_send, :on_super, etc. They will be called# during AST traversal and try to call corresponding methods on cops.# A call to `super` is used# to continue iterating over the children of a node.# However, if we know that a certain node type (like `int`) never has# child nodes, there is no reason to pay the cost of calling `super`.Parser::Meta::NODE_TYPES.eachdo|node_type|method_name=:"on_#{node_type}"nextunlessmethod_defined?(method_name)# Hacky: Comment-out code as neededr='#'unlessRESTRICTED_CALLBACKS.include?(method_name)# has Restricted?c='#'ifNO_CHILD_NODES.include?(node_type)# has Children?class_eval(<<~RUBY,__FILE__,__LINE__+1)
def on_#{node_type}(node) # def on_send(node)
trigger_responding_cops(:on_#{node_type}, node) # trigger_responding_cops(:on_send, node)
#{r} trigger_restricted_cops(:on_#{node_type}, node) # trigger_restricted_cops(:on_send, node)
#{c} super(node) # super(node)
#{c} trigger_responding_cops(:after_#{node_type}, node) # trigger_responding_cops(:after_send, node)
#{c}#{r} trigger_restricted_cops(:after_#{node_type}, node) # trigger_restricted_cops(:after_send, node)
end # end
RUBYend# @return [InvestigationReport]definvestigate(processed_source)reset@cops.each{|cop|cop.send:begin_investigation,processed_source}ifprocessed_source.valid_syntax?invoke(:on_new_investigation,@cops)invoke(:investigate,@forces,processed_source)walk(processed_source.ast)unless@cops.empty?invoke(:on_investigation_end,@cops)elseinvoke(:on_other_file,@cops)endreports=@cops.map{|cop|cop.send(:complete_investigation)}InvestigationReport.new(processed_source,reports,@errors)endprivatedeftrigger_responding_cops(callback,node)@callbacks[callback]&.eachdo|cop|with_cop_error_handling(cop,node)docop.public_send(callback,node)endendenddefreset@errors=[]enddefinitialize_callbacks@callbacks=build_callbacks(@cops)@restricted_map=restrict_callbacks(@callbacks)enddefbuild_callbacks(cops)callbacks={}cops.eachdo|cop|cop.callbacks_needed.eachdo|callback|(callbacks[callback]||=[])<<copendendcallbacksenddefrestrict_callbacks(callbacks)restricted={}RESTRICTED_CALLBACKS.eachdo|callback|restricted[callback]=restricted_map(callbacks[callback])endrestrictedenddeftrigger_restricted_cops(event,node)name=node.method_name@restricted_map[event][name]&.eachdo|cop|with_cop_error_handling(cop,node)docop.public_send(event,node)endendend# NOTE: mutates `callbacks` in placedefrestricted_map(callbacks)map={}callbacks&.select!do|cop|restrictions=cop.class.send:restrict_on_sendrestrictions.eachdo|name|(map[name]||=[])<<copendrestrictions.empty?endmapenddefinvoke(callback,cops,*args)cops.eachdo|cop|with_cop_error_handling(cop)docop.send(callback,*args)endendend# Allow blind rescues here, since we're absorbing and packaging or# re-raising exceptions that can be raised from within the individual# cops' `#investigate` methods.defwith_cop_error_handling(cop,node=nil)yieldrescueStandardError=>eraiseeif@options[:raise_error]err=ErrorWithAnalyzedFileLocation.new(cause: e,node: node,cop: cop)@errors<<errendendendend