Skip to content

Enable Style/FrozenStringLiteralComment - #40

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

With the # frozen_string_literal: true pragma, Ruby raises a FrozenError if code mutates a string literal by accident. The pragma also lets identical literals share one object instead of allocating a new string each time the line runs. Half of lib/ already had the pragma (lib/code_teams.rb, testing.rb, testing/rspec_helpers.rb, utils.rb, and the bin/* binstubs), but the cop was turned off with the note "Disabling for now until it's clearer why we want this". This PR turns it back on so every file behaves the same way.

Config change

  • .rubocop.yml: removed the Style/FrozenStringLiteralComment: Enabled: false block and its comment. The cop now runs with its default EnforcedStyle: always. The repo has no .rubocop_todo.yml, and the existing AllCops: Exclude list (vendor/bundle/**, bin/**) is unchanged.

Files that got the pragma (13)

  • Gemfile, Rakefile, code_teams.gemspec
  • lib/code_teams/plugin.rb, lib/code_teams/plugins/identity.rb
  • spec/spec_helper.rb, spec/support/io_helpers.rb, and the 6 spec files under spec/

rubocop -A --only Style/FrozenStringLiteralComment added the pragmas, and rubocop -a --only Layout/EmptyLineAfterMagicComment fixed the blank lines after them. Where a file has a Sorbet sigil, the pragma goes directly above # typed:, matching the files that already had it. srb tc --print=file-table-json confirms Sorbet still reads each file's strictness level (plugin.rb strict, identity.rb true, Rakefile ignore).

No new exclusions:

  • bin/* binstubs are generated by Bundler, already carry the pragma, and were already excluded.
  • sorbet/rbi/todo.rbi is generated, and rubocop's default Include doesn't cover .rbi files, so I left it alone.

Runtime fixes

None were needed. No spec failed, and the static and dynamic checks below found no string literal that gets mutated. In the two lib/ files that just got the pragma, the only frozen literals are 'name':

  • lib/code_teams/plugin.rb:69 and lib/code_teams/plugins/identity.rb:15 use it only as a hash key.
  • lib/code_teams/plugins/identity.rb:31 passes it to missing_key_error_message, which only interpolates it.

The strings these files return to callers (plugin.rb:56, identity.rb:28) are interpolated. Those are not frozen on Ruby >= 3.0, and the gem requires >= 3.3, so callers can still append to validation error messages.

Verification

  • Specs: 38 examples, 0 failures both before (main) and after, on Ruby 4.0.5. The branch also passes on Ruby 3.3.11 and 3.4.11 (the full CI matrix). All runs used RUBYOPT=-W:deprecated, with no FrozenError and no chilled-string warnings. The suite also passes with RUBYOPT=--enable-frozen-string-literal on all three Rubies.
  • RuboCop: 17 files inspected, no offenses. srb tc: no errors.
  • Static sweep: I read every Ruby file and ran a Prism scan listing each string literal, default argument, constant and mutating call. I also grepped for every mutating String method, []=, StringIO, IO buffer arguments and String.new. Every <</[]=/prepend hit is on an Array, Hash or Module, never a String.
  • Dynamic checks: Specs never reach some paths, including Plugin.missing_key_error_message, the nil-name branch of Identity.validation_errors, tag_value_for/to_tag, Team.from_hash, and the default data_accessor_name. A differential probe ran these against both main and this branch on all three Rubies (including with Sorbet runtime checks off, so the nil-name branch really runs). Results matched, and every returned string could still be appended to.
  • Build tooling: rake -T, rake build, gem build, Gem::Specification.load(...).validate, and a simulated rake release against a local bare remote all worked with the pragma in the Gemfile, Rakefile and gemspec.
  • Fresh Eyes: a local pre-push review found 0 blockers, 0 major, 0 minor and 0 info findings.

No version bump.

Remove the .rubocop.yml entry that disabled the cop, so it runs with
its default EnforcedStyle (always).

Add "# frozen_string_literal: true" to the 13 files the cop flagged:
Gemfile, Rakefile, code_teams.gemspec, lib/code_teams/plugin.rb,
lib/code_teams/plugins/identity.rb, and every spec file. Where the
pragma sits above a Sorbet "# typed:" sigil it comes first, matching
the lib files that already had it. Blank lines after the magic comments
satisfy Layout/EmptyLineAfterMagicComment.

No mutation fixes were needed. The newly frozen literals are only used
as hash keys, gemspec/Gemfile values, and read-only spec inputs.
Strings the library hands back to callers (validation errors,
missing_key_error_message) are interpolated, so they stay mutable on
the supported Rubies (>= 3.3).
@dduugg
dduugg requested a review from a team as a code owner September 26, 2026 16:40
@github-project-automation github-project-automation Bot moved this to Triage in Modularity Sep 26, 2026
@dduugg
dduugg merged commit ecbb734 into main Sep 26, 2026
9 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