Skip to content

Enable Style/FrozenStringLiteralComment - #181

Merged
dduugg merged 1 commit into
mainfrom
enable-frozen-string-literal-comment
Sep 26, 2026
Merged

dduugg merged 1 commit into
mainfrom
enable-frozen-string-literal-comment

Conversation

@dduugg

@dduugg dduugg commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

Why

# frozen_string_literal: true makes string literals immutable. Accidental in-place mutation of a literal then fails loudly with a FrozenError instead of silently changing shared state. Each evaluation of a literal also stops allocating a fresh String. Everything under lib/, plus the Rakefile and ext/code_ownership/extconf.rb, already had the pragma. This PR turns the cop on so the rest of the repo follows the same rule and new files can't drift.

Config change

.rubocop.yml: removed the block that disabled the cop:

# Disabling for now until it's clearer why we want this
Style/FrozenStringLiteralComment:
  Enabled: false

EnforcedStyle stays at the default (always). No Exclude or .rubocop_todo.yml was needed.

Files that got the pragma (11)

  • Gemfile
  • code_ownership.gemspec
  • bin/codeownership (after the shebang, before # typed: strict; checked that literals are frozen under both parse.y and Prism)
  • rakelib/compile.rake, rakelib/env.rake, rakelib/helpers.rake, rakelib/pkg.rake, rakelib/spec.rake
  • spec/spec_helper.rb, spec/support/application_fixtures.rb, spec/lib/code_ownership/cli_spec.rb

Exclusions: none. The generated Sorbet RBIs under sorbet/rbi/ aren't RuboCop targets (rubocop --list-target-files) and are never loaded at runtime, so they were left alone.

Runtime fixes

None were needed. No newly frozen literal reaches a mutating call. The only in-place string mutation in the repo is messages.last << "\n" at lib/code_ownership/private/for_file_output_builder.rb:73. Its receiver is always an interpolated string (L70 or L79). Interpolated strings are not frozen on Ruby >= 3.0, and the gemspec requires >= 3.3. That file also already had the pragma on main.

Verification

  • Specs: bundle exec rake (the CI command: env:dev, compile, spec) on Ruby 4.0.7. Before and after: 91 examples, 0 failures, 1 pending. The pending example is the Ruby < 3.4 backtrace-format case, pending by design on 3.4+. With RUBYOPT=-W:deprecated there were no "literal string will be frozen" (chilled string) warnings. The suite also passes on Ruby 3.3.11, the CI floor. It passes too under RUBYOPT="--enable-frozen-string-literal --debug-frozen-string-literal", which freezes every literal in every file, gems included.
  • RuboCop: no new offenses; Style/FrozenStringLiteralComment reports none. The one remaining offense, spec/lib/code_ownership_spec.rb:572 Style/RegexpLiteral, is already on main and is out of scope here.
  • Sorbet: srb tc is clean.
  • Static sweep: I traced every literal in the 11 files to where it ends up, including into library code: Rake, rake-compiler 1.3.1, rb_sys 0.9.130, RubyGems and Bundler's DSL, OptionParser, FileUtils, and the Rust extension, whose entry points take owned String and Vec<String>, so Ruby strings are copied, never mutated. None of them is mutated.
  • Dynamic checks, all with every literal frozen:
    • Every CLI subcommand and flag (validate, for_file, for_team, version, help) against a fixture project, including combined, abbreviated and invalid flags.
    • The public API paths the suite doesn't cover, e.g. path_from_klass, remove_file_annotation! error branches, and PackOwnershipValidator#call via packwerk.
    • gem build; rake pkg:ruby with real cargo vendor; rake pkg:<platform>:test with a real gem install; the rake-compiler native/cross gem tasks that CD uses; rake -T, clean, clobber.
    • No FrozenError anywhere.
  • Fresh Eyes (local, pre-push): 0 blockers, 0 majors, 1 minor. The minor was that the description didn't say why the cop is being enabled, given the removed "until it's clearer why we want this" note. The "Why" section above covers it.

Unrelated and not changed here: rake pkg:ruby:test (installing the source gem) fails during the native build with a cargo JSON parse error. It fails the same way on main.

  • I bumped the gem version (or don't need to): no bump. This is a lint config change plus magic comments, with no runtime behavior change for gem users.

Remove the `Style/FrozenStringLiteralComment: Enabled: false` entry from
.rubocop.yml so the cop runs with its default EnforcedStyle
(always).

Add `# frozen_string_literal: true` (via `rubocop -A --only
Style/FrozenStringLiteralComment`, plus the blank line
Layout/EmptyLineAfterMagicComment wants) to the 11 files that lacked it:
Gemfile, code_ownership.gemspec, bin/codeownership, rakelib/*.rake,
spec/spec_helper.rb, spec/support/application_fixtures.rb and
spec/lib/code_ownership/cli_spec.rb. In bin/codeownership the pragma
goes after the shebang and before the `# typed: strict` sigil. lib/,
the Rakefile and extconf.rb already had it.

No mutation fixes were needed. A static review of every string-mutating
call in lib/, bin/, rakelib/, the gemspec, Gemfile and the specs found
no mutation of a frozen literal. The one in-place append,
`messages.last << "\n"` in ForFileOutputBuilder, always receives an
interpolated (unfrozen) string, and the gem requires Ruby >= 3.3. The
spec suite, the CLI, `rake pkg:ruby` and `gem build` also pass with the
pragma, and the suite also passes under --enable-frozen-string-literal.
@dduugg
dduugg requested a review from a team as a code owner September 26, 2026 16:55
@dduugg
dduugg merged commit cf1bbc4 into main Sep 26, 2026
14 checks passed
@dduugg
dduugg deleted the enable-frozen-string-literal-comment branch September 26, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant