Class: RuboCop::Cop::Team
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Inherits: | Object |
| Defined in: | lib/rubocop/cop/team.rb |
Overview
A group of cops, ready to be called on duty to inspect files. Team is responsible for selecting only relevant cops to be sent on duty, as well as insuring that the needed forces are sent along with them.
For performance reasons, Team will first dispatch cops & forces in two groups, first the ones needed for autocorrection (if any), then the rest (unless autocorrections happened).
Class Method Summary
Instance Attribute Summary
- #autocorrect? ⇒ Boolean readonly
- #cops readonly
- #debug? ⇒ Boolean readonly
-
#defer_corrections
rw
Internal use only
When set to true, the corrected source is not written back to the inspected file; it is exposed through #updated_source instead.
- #errors readonly
-
#standby_registry=(value)
writeonly
Internal use only
Registry used to mobilize cops that were not instantiated because they are disabled in the config, when a comment directive opts them back in for a file.
-
#updated_source
readonly
Internal use only
The corrected source of the last investigation, if corrections were made with #defer_corrections enabled.
-
#updated_source_file?
readonly
Alias for #updated_source_file.
- #warnings readonly
Instance Method Summary
- #external_dependency_checksum
- #forces deprecated Deprecated.
- #initialize(cops, config = nil, options = {}) ⇒ Team constructor
- #inspect_file(processed_source)
- #investigate(processed_source, offset: 0, original: processed_source) ⇒ Commissioner::InvestigationReport
- #investigate_fragments(fragments, original:) ⇒ Array<Offense>
- #updated_source_file (also: #updated_source_file?) readonly
- #apply_correction(processed_source, new_source) private
- #autocorrect(processed_source, corrector) readonly private
- #be_ready private
- #collate_corrections(report, offset:, original:) private
- #collated_corrector(report, offset:, original:) private
- #each_corrector(report) private
- #handle_error(error, location, cop) private
- #handle_warning(error, location) private
- #investigate_fragment(fragment, original, data) private
- #investigate_partial(cops, processed_source, offset:, original:) ⇒ Commissioner::InvestigationReport private
- #investigate_with_corrector(processed_source, offset:, original:) private
- #merge_corrector!(corrector, to_merge, offset:) private
-
#opted_in_standby_cops(processed_source) ⇒ Array<cop>
private
Cops that were not mobilized because they are disabled in the config, but are opted back in for the given file by an
enablecomment directive. - #process_errors(file, errors) private
- #reset private
- #roundup_relevant_cops(processed_source) ⇒ Array<cop> private
- #standby_cop(cop_name) private
- #support_target_rails_version?(cop) ⇒ Boolean private
- #support_target_ruby_version?(cop) ⇒ Boolean private
- #suppress_clobbering private
- #validate_config private
Constructor Details
.new(cop_or_classes, config, options = {}) ⇒ Team
# File 'lib/rubocop/cop/team.rb', line 18
def self.new(cop_or_classes, config, = {}) # Support v0 api: if cop_or_classes.first.is_a?(Class) warn Rainbow(<<~WARNING).yellow, uplevel: 1 `Team.new` with cop classes is deprecated. Use `Team.mobilize` instead. WARNING return mobilize(cop_or_classes, config, ) end super end
#initialize(cops, config = nil, options = {}) ⇒ Team
Class Method Details
.forces_for(cops) ⇒ Array<Force>
# File 'lib/rubocop/cop/team.rb', line 55
def self.forces_for(cops) needed = Hash.new { |h, k| h[k] = [] } cops.each do |cop| forces = cop.class.joining_forces if forces.is_a?(Array) forces.each { |force| needed[force] << cop } elsif forces needed[forces] << cop end end needed.map { |force_class, joining_cops| force_class.new(joining_cops) } end
.mobilize(cop_classes, config, options = {}) ⇒ Team
# File 'lib/rubocop/cop/team.rb', line 31
def self.mobilize(cop_classes, config, = {}) if cop_classes.is_a?(Registry) # Instantiate only the enabled cops so that disabled lazy-loaded cops are not loaded. # The registry is kept on standby to mobilize a disabled cop when a comment directive # opts it back in. cops = cop_classes.enabled(config).map { |cop_class| cop_class.new(config, ) } team = new(cops, config, ) team.standby_registry = cop_classes team else new(mobilize_cops(cop_classes, config, ), config, ) end end
.mobilize_cops(cop_classes, config, options = {}) ⇒ Array<Cop::Base>
Instance Attribute Details
#autocorrect? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/cop/team.rb', line 102
def autocorrect? @options[:autocorrect] end
#cops (readonly)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 69
attr_reader :errors, :warnings, :updated_source_file, :cops
#debug? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/cop/team.rb', line 106
def debug? @options[:debug] end
#defer_corrections (rw)
When set to true, the corrected source is not written back to the inspected file; it is exposed through #updated_source instead.
# File 'lib/rubocop/cop/team.rb', line 74
attr_accessor :defer_corrections
#errors (readonly)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 69
attr_reader :errors, :warnings, :updated_source_file, :cops
#standby_registry=(value) (writeonly)
Registry used to mobilize cops that were not instantiated because they are disabled in the config, when a comment directive opts them back in for a file.
# File 'lib/rubocop/cop/team.rb', line 85
attr_writer :standby_registry
#updated_source (readonly)
The corrected source of the last investigation, if corrections were made with #defer_corrections enabled.
# File 'lib/rubocop/cop/team.rb', line 79
attr_reader :updated_source
#updated_source_file? (readonly)
Alias for #updated_source_file.
# File 'lib/rubocop/cop/team.rb', line 87
alias updated_source_file? updated_source_file
#warnings (readonly)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 69
attr_reader :errors, :warnings, :updated_source_file, :cops
Instance Method Details
#apply_correction(processed_source, new_source) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 174
def apply_correction(processed_source, new_source) if @options[:stdin] # holds source read in from stdin, when --stdin option is used @options[:stdin] = new_source elsif defer_corrections @updated_source = new_source else File.write(processed_source.file_path, new_source) end end
#autocorrect(processed_source, corrector) (readonly, private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 163
def autocorrect(processed_source, corrector) @updated_source_file = false @updated_source = nil return unless autocorrect? return unless corrector return if corrector.empty? apply_correction(processed_source, corrector.rewrite) @updated_source_file = true end
#be_ready (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 185
def be_ready return if @ready reset @cops.map!(&:ready) @standby_cops.transform_values! { |cop| cop&.ready } @ready = true end
#collate_corrections(report, offset:, original:) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 315
def collate_corrections(report, offset:, original:) corrector = Corrector.new(original) each_corrector(report) do |to_merge| merge_corrector!(corrector, to_merge, offset: offset) end corrector end
#collated_corrector(report, offset:, original:) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 306
def collated_corrector(report, offset:, original:) return unless autocorrect? return if report.processed_source.parser_error corrector = collate_corrections(report, offset: offset, original: original) corrector unless corrector.empty? end
#each_corrector(report) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 335
def each_corrector(report) skips = Set.new report.cop_reports.each do |cop_report| cop = cop_report.cop corrector = cop_report.corrector next if corrector.nil? || corrector.empty? next if skips.include?(cop.class) yield corrector skips.merge(cop.class.autocorrect_incompatible_with) end end
#external_dependency_checksum
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 152
def external_dependency_checksum # The external dependency checksums are cached per RuboCop team so that # the checksums don't need to be recomputed for each file. @external_dependency_checksum ||= begin keys = cops.filter_map(&:external_dependency_checksum) Digest::SHA1.hexdigest(keys.join) end end
#forces
# File 'lib/rubocop/cop/team.rb', line 144
def forces warn Rainbow(<<~WARNING).yellow, uplevel: 1 `forces` is deprecated. WARNING @forces ||= self.class.forces_for(cops) end
#handle_error(error, location, cop) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 387
def handle_error(error, location, cop) = Rainbow("An error occurred while #{cop.name} cop was inspecting #{location}.").red @errors << warn if debug? puts error. else warn 'To see the complete backtrace run rubocop -d.' end end
#handle_warning(error, location) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 379
def handle_warning(error, location) = Rainbow("#{error.} (from file: #{location})").yellow @warnings << warn puts error.backtrace if debug? end
#inspect_file(processed_source)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 112
def inspect_file(processed_source) warn Rainbow(<<~WARNING).yellow, uplevel: 1 `inspect_file` is deprecated. Use `investigate` instead. WARNING investigate(processed_source).offenses end
#investigate(processed_source, offset: 0, original: processed_source) ⇒ Commissioner::InvestigationReport
# File 'lib/rubocop/cop/team.rb', line 121
def investigate(processed_source, offset: 0, original: processed_source) result = investigate_with_corrector(processed_source, offset: offset, original: original) autocorrect(processed_source, result.corrector) result.report end
#investigate_fragment(fragment, original, data) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 235
def investigate_fragment(fragment, original, data) offenses, errors, warnings, corrector = data result = investigate_with_corrector( fragment[:processed_source], offset: fragment[:offset], original: original ) offenses.concat(result.report.offenses) if result.corrector corrector ||= Corrector.new(original) merge_corrector!(corrector, result.corrector, offset: 0) data[3] = corrector end errors.concat(@errors) warnings.concat(@warnings) end
#investigate_fragments(fragments, original:) ⇒ Array<Offense>
# File 'lib/rubocop/cop/team.rb', line 128
def investigate_fragments(fragments, original:) @updated_source_file = false offenses, errors, warnings, corrector = fragments.each_with_object([[], [], [], nil]) do |fragment, data| investigate_fragment(fragment, original, data) end autocorrect(original, corrector) @errors = errors @warnings = warnings offenses end
#investigate_partial(cops, processed_source, offset:, original:) ⇒ Commissioner::InvestigationReport (private)
# File 'lib/rubocop/cop/team.rb', line 200
def investigate_partial(cops, processed_source, offset:, original:) commissioner = Commissioner.new(cops, self.class.forces_for(cops), @options) commissioner.investigate(processed_source, offset: offset, original: original) end
#investigate_with_corrector(processed_source, offset:, original:) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 205
def investigate_with_corrector(processed_source, offset:, original:) be_ready # The autocorrection process may have to be repeated multiple times # until there are no corrections left to perform # To speed things up, run autocorrecting cops by themselves, and only # run the other cops when no corrections are left on_duty = roundup_relevant_cops(processed_source) autocorrect_cops, other_cops = on_duty.partition(&:autocorrect?) report = investigate_partial(autocorrect_cops, processed_source, offset: offset, original: original) corrector = collated_corrector(report, offset: offset, original: original) unless corrector # If we corrected some errors, another round of inspection will be # done, and any other offenses will be caught then, so only need # to check other_cops if no correction was done report = report.merge(investigate_partial(other_cops, processed_source, offset: offset, original: original)) end process_errors(processed_source.path, report.errors) InvestigationResult.new(report, corrector) ensure @ready = false end
#merge_corrector!(corrector, to_merge, offset:) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 325
def merge_corrector!(corrector, to_merge, offset:) suppress_clobbering do if corrector.source_buffer == to_merge.source_buffer corrector.merge!(to_merge) else corrector.import!(to_merge, offset: offset) end end end
#opted_in_standby_cops(processed_source) ⇒ Array<cop> (private)
Cops that were not mobilized because they are disabled in the config,
but are opted back in for the given file by an enable comment directive.
Their classes are loaded on demand.
# File 'lib/rubocop/cop/team.rb', line 269
def opted_in_standby_cops(processed_source) return [] unless @standby_registry opted_in_names = processed_source.comment_config.opt_in_cops return [] if opted_in_names.empty? @mobilized_cop_names ||= Set.new(cops.map(&:cop_name)) opted_in_names.filter_map do |cop_name| next if @mobilized_cop_names.include?(cop_name) standby_cop(cop_name) end end
#process_errors(file, errors) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 362
def process_errors(file, errors) errors.each do |error| line = ":#{error.line}" if error.line column = ":#{error.column}" if error.column location = "#{file}#{line}#{column}" cause = error.cause if cause.is_a?(Warning) handle_warning(cause, location) elsif cause.is_a?(Force::HookError) handle_error(cause.cause, location, cause.joining_cop) else handle_error(cause, location, error.cop) end end end
#reset (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 194
def reset @errors = [] @warnings = [] end
#roundup_relevant_cops(processed_source) ⇒ Array<cop> (private)
# File 'lib/rubocop/cop/team.rb', line 254
def roundup_relevant_cops(processed_source) (cops + opted_in_standby_cops(processed_source)).select do |cop| next false if cop.excluded_file?(processed_source.file_path) next true if processed_source.comment_config.cop_opted_in?(cop) next false unless @registry.enabled?(cop, @config) support_target_ruby_version?(cop) && support_target_rails_version?(cop) end end
#standby_cop(cop_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 283
def standby_cop(cop_name) return @standby_cops[cop_name] if @standby_cops.key?(cop_name) cop_class = @standby_registry.find_by_cop_name(cop_name) @standby_cops[cop_name] = cop_class&.new(@config, @options) end
#support_target_rails_version?(cop) ⇒ Boolean (private)
# File 'lib/rubocop/cop/team.rb', line 297
def support_target_rails_version?(cop) # In this case, the rails version was already checked by `#excluded_file?` return true if defined?(RuboCop::Rails::TargetRailsVersion::USES_REQUIRES_GEM_API) return true unless cop.class.respond_to?(:support_target_rails_version?) cop.class.support_target_rails_version?(cop.target_rails_version) end
#support_target_ruby_version?(cop) ⇒ Boolean (private)
# File 'lib/rubocop/cop/team.rb', line 291
def support_target_ruby_version?(cop) return true unless cop.class.respond_to?(:support_target_ruby_version?) cop.class.support_target_ruby_version?(cop.target_ruby_version) end
#suppress_clobbering (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 350
def suppress_clobbering yield rescue ::Parser::ClobberingError # ignore Clobbering errors end
#updated_source_file (readonly) Also known as: #updated_source_file?
[ GitHub ]#validate_config (private)
[ GitHub ]# File 'lib/rubocop/cop/team.rb', line 356
def validate_config cops.each do |cop| cop.validate_config if cop.respond_to?(:validate_config) end end