# frozen_string_literal: truemoduleRuboCop# The kind of Ruby that code inspected by RuboCop is written in.# @api privateclassTargetRubyKNOWN_RUBIES=[2.0,2.1,2.2,2.3,2.4,2.5,2.6,2.7,3.0,3.1,3.2,3.3,3.4,3.5].freezeDEFAULT_VERSION=2.7OBSOLETE_RUBIES={1.9=>'0.41',2.0=>'0.50',2.1=>'0.57',2.2=>'0.68',2.3=>'0.81',2.4=>'1.12',2.5=>'1.28',2.6=>'1.50'}.freezeprivate_constant:KNOWN_RUBIES,:OBSOLETE_RUBIES# A place where information about a target ruby version is found.# @api privateclassSourceattr_reader:version,:namedefinitialize(config)@config=config@version=find_versionenddefto_snameendend# The target ruby version may be configured by setting the# `RUBOCOP_TARGET_RUBY_VERSION` environment variable.classRuboCopEnvVar<Sourcedefname'`RUBOCOP_TARGET_RUBY_VERSION` environment variable'endprivatedeffind_versionENV.fetch('RUBOCOP_TARGET_RUBY_VERSION',nil)&.to_fendend# The target ruby version may be configured in RuboCop's config.# @api privateclassRuboCopConfig<Sourcedefname"`TargetRubyVersion` parameter (in #{@config.smart_loaded_path})"endprivatedeffind_version@config.for_all_cops['TargetRubyVersion']&.to_fendend# The target ruby version may be found in a .gemspec file.# @api privateclassGemspecFile<SourceextendNodePattern::Macros# @!method required_ruby_version(node)def_node_search:required_ruby_version,<<~PATTERN
(send _ :required_ruby_version= $_)
PATTERN# @!method gem_requirement_versions(node)def_node_matcher:gem_requirement_versions,<<~PATTERN
(send (const(const _ :Gem):Requirement) :new
{$str+ | (send $str :freeze)+ | (array $str+) | (array (send $str :freeze)+)}
)
PATTERNdefname"`required_ruby_version` parameter (in #{gemspec_filepath})"endprivatedeffind_versionfile=gemspec_filepathreturnunlessfile&&File.file?(file)right_hand_side=version_from_gemspec_file(file)returnifright_hand_side.nil?find_minimal_known_ruby(right_hand_side)enddefgemspec_filepathreturn@gemspec_filepathifdefined?(@gemspec_filepath)@gemspec_filepath=@config.traverse_directories_upwards(@config.base_dir_for_path_parameters)do|dir|# NOTE: Can't use `dir.glob` because of JRuby 9.4.8.0 incompatibility:# https://github.com/jruby/jruby/issues/8358candidates=Pathname.glob("#{dir}/*.gemspec")# Bundler will use a gemspec whatever the filename is, as long as its the only one in# the folder.breakcandidates.firstifcandidates.one?endenddefversion_from_gemspec_file(file)processed_source=ProcessedSource.from_file(file,DEFAULT_VERSION,parser_engine: @config.parser_engine)returnunlessprocessed_source.valid_syntax?required_ruby_version(processed_source.ast).firstenddefversion_from_right_hand_side(right_hand_side)gem_requirement_versions=gem_requirement_versions(right_hand_side)ifright_hand_side.array_type?&&right_hand_side.children.all?(&:str_type?)version_from_array(right_hand_side)elsifgem_requirement_versionsgem_requirement_versions.map(&:value)elsifright_hand_side.str_type?right_hand_side.valueendenddefversion_from_array(array)array.children.map(&:value)enddeffind_minimal_known_ruby(right_hand_side)version=version_from_right_hand_side(right_hand_side)returnunlessversionrequirement=Gem::Requirement.new(version)KNOWN_RUBIES.detectdo|v|requirement.satisfied_by?(Gem::Version.new("#{v}.99"))endendend# The target ruby version may be found in a .ruby-version file.# @api privateclassRubyVersionFile<SourceRUBY_VERSION_FILENAME='.ruby-version'RUBY_VERSION_PATTERN=/\A(?:ruby-)?(?<version>\d+\.\d+)/.freezedefname"`#{RUBY_VERSION_FILENAME}`"endprivatedeffilenameRUBY_VERSION_FILENAMEenddefpatternRUBY_VERSION_PATTERNenddeffind_versionfile=version_filereturnunlessfile&&File.file?(file)File.read(file).match(pattern){|md|md[:version].to_f}enddefversion_file@version_file||=@config.find_file_upwards(filename,@config.base_dir_for_path_parameters)endend# The target ruby version may be found in a .tool-versions file, in a line# starting with `ruby`.# @api privateclassToolVersionsFile<RubyVersionFileTOOL_VERSIONS_FILENAME='.tool-versions'TOOL_VERSIONS_PATTERN=/^(?:ruby )(?<version>\d+\.\d+)/.freezedefname"`#{TOOL_VERSIONS_FILENAME}`"endprivatedeffilenameTOOL_VERSIONS_FILENAMEenddefpatternTOOL_VERSIONS_PATTERNendend# The lock file of Bundler may identify the target ruby version.# @api privateclassBundlerLockFile<Sourcedefname"`#{bundler_lock_file_path}`"endprivatedeffind_versionlock_file_path=bundler_lock_file_pathreturnnilunlesslock_file_pathin_ruby_section=falseFile.foreach(lock_file_path)do|line|# If ruby is in Gemfile.lock or gems.lock, there should be two lines# towards the bottom of the file that look like:# RUBY VERSION# ruby W.X.YpZ# We ultimately want to match the "ruby W.X.Y.pZ" line, but there's# extra logic to make sure we only start looking once we've seen the# "RUBY VERSION" line.in_ruby_section||=line.match(/^\s*RUBY\s*VERSION\s*$/)nextunlessin_ruby_section# We currently only allow this feature to work with MRI ruby. If# jruby (or something else) is used by the project, it's lock file# will have a line that looks like:# RUBY VERSION# ruby W.X.YpZ (jruby x.x.x.x)# The regex won't match in this situation.result=line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)returnresult.captures.first.to_fifresultendenddefbundler_lock_file_path@config.bundler_lock_file_pathendend# If all else fails, a default version will be picked.# @api privateclassDefault<Sourcedefname'default'endprivatedeffind_versionDEFAULT_VERSIONendenddefself.supported_versionsKNOWN_RUBIESendSOURCES=[RuboCopEnvVar,RuboCopConfig,GemspecFile,RubyVersionFile,ToolVersionsFile,BundlerLockFile,Default].freezeprivate_constant:SOURCESdefinitialize(config)@config=configenddefsource@source||=SOURCES.each.lazy.map{|c|c.new(@config)}.detect(&:version)enddefversionsource.versionenddefsupported?KNOWN_RUBIES.include?(version)enddefrubocop_version_with_supportifsupported?RuboCop::Version::STRINGelseOBSOLETE_RUBIES[version]endendendend