skip to content

In RuboCop, what does AllCops TargetRubyVersion change, and where does RuboCop find the version when .rubocop.yml does not set it?

level: middleimportance: should knowfreq 40%

answer

  1. the oldest Ruby the code supports
  2. picks the parser grammar
  3. version-gated cops switch on or off
  4. gemspec, .ruby-version, Gemfile.lock
  5. falls back to 2.7

basics

~10 s

TargetRubyVersion tells RuboCop the oldest Ruby the code must run on; it selects the parser grammar and which version-gated cops apply. Unset, RuboCop reads the gemspec, .ruby-version, mise.toml, .tool-versions or Gemfile.lock, else assumes 2.7.

solid answer

~40 s

`AllCops: TargetRubyVersion` is a `major.minor` number such as `3.4` or `4.0`, meaning the **lowest** Ruby the inspected code must run on. It does two things: RuboCop parses files with that version's grammar, so syntax newer than the target is reported as a `Lint/Syntax` offense with a hint to set `TargetRubyVersion`; and cops declare a minimum target, so `Style/EndlessMethod` needs 3.0 and `Style/ItBlockParameter` needs 3.4. When the key is missing, RuboCop 1.91 tries, in order: the `RUBOCOP_TARGET_RUBY_VERSION` environment variable (which beats the config file too), `.rubocop.yml`, the lowest version satisfying `required_ruby_version` in the project's gemspec, `.ruby-version`, `mise.toml`, `.tool-versions`, and the `RUBY VERSION` in `Gemfile.lock`. With none of those it assumes **2.7**.

code

yaml · 3 lines
yaml
# .rubocop.yml of an application deployed on Ruby 4.0
AllCops:
  TargetRubyVersion: 4.0

go deeper

for a junior

Recall that TargetRubyVersion lives under AllCops, is written as major.minor, and describes the Ruby your code runs on.

for a middle

Explain its two effects, the parser grammar and version-gated cops, and the fallback chain from gemspec and .ruby-version down to 2.7.

for a senior

Recognise the Lint/Syntax symptom of a wrong target, set the lowest supported version for gems, and bump the target with each Ruby upgrade.

for a principal

Tie the target to the support policy: raising it is a promise to drop older Rubies, so it should move with the gemspec, not ahead of it.

## What the setting means `TargetRubyVersion` lives under `AllCops` and names the **oldest** Ruby release the inspected code is supposed to run on, written as `major.minor` without the patch level: `3.3`, `3.4`, `4.0`. For an application that runs on exactly one Ruby this is simply that version. For a gem that supports a range it is the **bottom** of the range, because RuboCop must not suggest syntax the oldest supported Ruby cannot parse. ## What it changes 1. **The parser grammar.** RuboCop parses each file as the target version would. Code using syntax newer than the target - an endless method (3.0), hash shorthand `{x:, y:}` (3.1), the `it` block parameter (3.4) - can be reported as a `Lint/Syntax` offense whose message names the parser version it used (for example the Ruby 2.7 parser) and says to configure `TargetRubyVersion` under `AllCops`. 2. **Which cops apply.** A cop can declare `minimum_target_ruby_version`; below it the cop stays silent. `Style/EndlessMethod` requires 3.0 and `Style/ItBlockParameter` requires 3.4, so a project targeting 2.7 never hears from them. 3. **What autocorrect may write.** Because cops only suggest constructs the target supports, raising the target is how a project opts into modern rewrites, and lowering it is how a gem avoids breaking older users. ## Where RuboCop looks when the key is missing RuboCop 1.91 checks these sources in order and uses the first that yields a version: | Order | Source | What it reads | |---|---|---| | 1 | `RUBOCOP_TARGET_RUBY_VERSION` env var | the number; overrides even `.rubocop.yml` | | 2 | `.rubocop.yml` | `AllCops: TargetRubyVersion` | | 3 | the project's `*.gemspec` | the lowest known Ruby satisfying `required_ruby_version` | | 4 | `.ruby-version` | `3.4.1` or `ruby-3.4.1`, truncated to `3.4` | | 5 | `mise.toml` | a `ruby = "..."` line | | 6 | `.tool-versions` | a `ruby ...` line | | 7 | `Gemfile.lock` | the `RUBY VERSION` section | | 8 | built-in default | `2.7` | A few details matter in practice: - the gemspec must state `required_ruby_version` as a literal string, array of strings or `Gem::Requirement.new(...)`; a value computed at run time (read from a file, a constant) is not detected, and RuboCop moves on to the next source; - the gemspec lookup uses the directory's only `*.gemspec`, the same rule Bundler follows; - the env var is meant for one-off runs, such as checking how a gem would lint on a newer Ruby. ## Failure modes - **Nothing found:** the 2.7 fallback means modern syntax may be reported as `Lint/Syntax` and version-gated cops never run. Setting the key explicitly removes the guesswork. - **Unknown or too old:** RuboCop 1.91 accepts targets from 2.0 up to 4.0, plus 4.1 as experimental. Anything else stops the run with a validation error that lists the supported versions; for 1.9 the message also says that 1.9-compatible analysis was dropped after RuboCop 0.41. - **Too new for the runtime:** the target describes the code, not the Ruby running RuboCop; a CI job can lint `4.0` code while running on any Ruby that runs RuboCop itself. ## Worked example A gem supports Ruby 3.2 and later and has no `TargetRubyVersion` in its `.rubocop.yml`: 1. The env var is unset and the config key is missing, so RuboCop reads the gemspec. 2. `required_ruby_version = ">= 3.2"` is a literal, so RuboCop walks its list of known versions and takes the first one that satisfies it: `3.2`. 3. Cops that need 3.3 or 3.4 stay quiet, and the parser accepts 3.2 syntax. 4. A contributor running `RUBOCOP_TARGET_RUBY_VERSION=4.0 rubocop` sees what the code would be told on 4.0, without changing any file. The `.ruby-version` a developer keeps for local work (say `4.0.7`) is never consulted here, because the gemspec comes earlier in the chain. That is intended: the gemspec states what users may run, while `.ruby-version` states what one developer happens to run. ## Recommended practice - Applications: set `TargetRubyVersion` to the deployed Ruby, or let `.ruby-version` supply it, and bump it in the same change as the Ruby upgrade. - Gems: keep `required_ruby_version` literal so RuboCop derives the lowest supported version automatically. - Monorepos: a sub-project with its own `.rubocop.yml` can set its own target.

  • Why does RuboCop report a syntax error on valid Ruby 3.4 code that uses it in a block?
    RuboCop parses with the grammar of the target version, not the Ruby that runs it. If no source sets a target, it falls back to 2.7, whose grammar predates the `it` block parameter, and the `Lint/Syntax` message says it used the Ruby 2.7 parser and points at `TargetRubyVersion` under `AllCops`. Setting `TargetRubyVersion: 3.4` or later, or adding `.ruby-version`, fixes it.
  • A gem supports Ruby 3.2 to 4.0. Which TargetRubyVersion should its RuboCop config use?
    3.2, the lowest supported version. RuboCop must not suggest syntax that 3.2 cannot parse, such as the `it` block parameter from 3.4. If the gemspec says `required_ruby_version = ">= 3.2"`, RuboCop derives 3.2 without any explicit setting.

saying these in an interview costs you the question

  • TargetRubyVersion should be the newest Ruby a gem supports, not the oldest.
  • RuboCop always uses the version of the Ruby interpreter running it as the target.
  • The RUBOCOP_TARGET_RUBY_VERSION variable is ignored when .rubocop.yml sets the key.
  • TargetRubyVersion should include the patch level, such as 3.4.1.