Enable Style/FrozenStringLiteralComment - #64
Merged
Merged
Conversation
Remove the .rubocop.yml entry that disabled Style/FrozenStringLiteralComment so the cop runs with its default EnforcedStyle (always). Add "# frozen_string_literal: true" to the 11 files the cop flagged: Gemfile, Rakefile, parse_packwerk.gemspec, every file under lib/ that lacked it, and spec/support/have_matching_package.rb. In files with a Sorbet "# typed:" sigil, the pragma goes on the line after the sigil, matching the existing package_set.rb. Drop the now-redundant .freeze calls on the string constants in lib/parse_packwerk/constants.rb (Style/RedundantFreeze). The literals are still frozen, now through the pragma. No mutation fixes were needed. The suite shows no FrozenError and no "literal string will be frozen" warnings under -W:deprecated, and a static sweep of lib/ and the build files found no string-mutating call on a literal. One visible effect: Package#public_path defaults to 'app/public', which is now a frozen String. Package.from already returned the shared DEFAULT_PUBLIC_PATH constant, so mutating it would have corrupted every package; now it raises instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
With
# frozen_string_literal: true, string literals are frozen. Code that accidentally mutates a literal (or a constant built from one) then raisesFrozenErrorright away instead of silently corrupting shared state. It also cuts allocations: each literal is allocated once and reused, not rebuilt every time the line runs. Ruby 3.4+ already warns about mutating these "chilled" literals, so enabling the pragma now puts this gem ahead of that default change.Config change
.rubocop.yml: deleted the block that disabled the cop:The cop now runs with its default
EnforcedStyle: always. Nothing else in the config changed.Files that got the pragma
11 files:
Gemfile,Rakefile,parse_packwerk.gemspec,lib/parse_packwerk.rb,lib/parse_packwerk/{configuration,constants,extensions,package,package_todo,violation}.rb, andspec/support/have_matching_package.rb.lib/parse_packwerk/package_set.rb,spec/spec_helper.rbandspec/parse_packwerk_spec.rbalready had it.# typed:sigil, the pragma goes on the line after the sigil, which is wherepackage_set.rbalready has it.srb tcpasses, and disassembling the files confirms Ruby honors the pragma in that position.lib/parse_packwerk/constants.rb:Style/RedundantFreezeflagged the explicit.freezeon the 12 string constants (ROOT_PACKAGE_NAME,PACKAGE_YML_NAME, and so on), so I removed it. The pragma now freezes them, so their frozenness and interning are unchanged.Gemfile,Rakefile, the gemspec,lib/**/*.rbandspec/**/*.rb. The generatedsorbet/rbi/**/*.rbifiles are never inspected or executed, so they need no pragma and noExcludeentry.Runtime fixes
None were needed. No path in the gem passes a frozen literal to a mutating call. The only
<<inlib/isArray#<<on a local accumulator (lib/parse_packwerk/package_todo.rb:29). Every other literal is only used as a hash key, aninclude?or==argument, aFile.openmode, aFile.join/Pathname#join/fnmatchargument, or an argument to the non-banggsubinwrite_package_yml!.One visible behavior change (not a bug)
DEFAULT_PUBLIC_PATH(lib/parse_packwerk/constants.rb:26,'app/public') is now frozen. It is thedefault:forPackage#public_path(lib/parse_packwerk/package.rb:12) and the fallback inPackage.from(lib/parse_packwerk/package.rb:32). As a result:Package.fromalready returned the shared constant object onmain. Mutating it there (for examplepkg.public_path << '/x') silently changed the default for every package. It now raisesFrozenError.Package.newwithoutpublic_path: sorbet-runtime used to deep-clone the unfrozen default for each instance. The default is frozen now, so it shares the constant, and mutating it raisesFrozenErrorinstead of changing a private copy.public_pathis aconston the struct and was never meant to be mutated. Nothing in this gem mutates it. I also checked the other rubyatscale gems that useParsePackwerk(packs, pack_stats, visualize_packs, query_packwerk, danger-packwerk, rubocop-packs): they only interpolate, join, compare, or pass the value back intoPackage.new/with.For the same reason, the elements of
ParsePackwerk.key_sort_orderand the default elements ofParsePackwerk.yml.package_paths('**/'and'.') are now frozen too. The arrays themselves stay mutable. No consumer mutates these elements.Verification
bundle exec rspecgives 55 examples, 0 failures both before (main) and after, on Ruby 4.0.5. On this branch the suite also passes on Ruby 3.3 and 3.4, the other CI versions. It passes underRUBYOPT="-W:deprecated --debug=frozen-string-literal"with no chilled-string warnings, and under--enable-frozen-string-literal(freeze everything globally).bundle exec rake(default task: spec) also passes.bundle exec rubocopinspected 14 files with no offenses.bundle exec srb tcreports no errors.gem build parse_packwerk.gemspec,rake buildandrake -Tall work with the pragma in the gemspec, Gemfile and Rakefile.<</concat/[]=,force_encoding, string buffers andStringIO, mutable default arguments, mutated constants, literals returned to callers that mutate them, and literals passed to libraries that mutate their argument. I found none. I also checked the library code these values flow into: sorbet-runtimeApplyDefault, PsychYAML.dump, and the RubyGems spec setters.all,find,package_from_pathwith a String or Pathname,violations,public_directory,yml,write_package_yml!withpreserve_key_ordertrue and false,bust_cache!,MissingConfiguration, andConfiguration.fetchwith a missing file, a comment-only file and a scalar exclude. The script also covered the paths the specs don't reach. I ran it against this branch and againstmain. The outputs match except for the frozenness changes described above.No version bump.