Module: Audition::Rewriters::MagicComments
- Defined in:
- lib/audition/rewriters.rb
Overview
-- magic comments ----------------------------------------------
Defined Under Namespace
Classes: ConstantValues
Constant Summary collapse
- SCV_LINE =
"# shareable_constant_value: literal\n"- FSL_LINE =
"# frozen_string_literal: true\n"
Class Method Summary collapse
- .comment_plan(file, line) ⇒ Object
- .constant_values(file) ⇒ Object
-
.deep_literal?(node, classifier) ⇒ Boolean
Bare literals only:
[...].freezeis a method call, and under the magic comment Ruby raises for it at assignment because the shallowly-frozen value is unshareable (verified on 4.0 with jwt's NAMED_CURVES). -
.plan(file, findings) ⇒ Object
shareable_constant_value: literalraises at load for non-literal constant values (verified on 4.0), so it is only planned when every constant assignment in the file is a literal all the way down: an array literal holding a local or a call (Racc-generated parser tables are the canonical case) becomes an unshareable value that the magic comment rejects at load time.
Class Method Details
.comment_plan(file, line) ⇒ Object
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/audition/rewriters.rb', line 74 def self.comment_plan(file, line) offset = file.magic_insertion_offset { edit: Autofix.new(start_offset: offset, end_offset: offset, replacement: line, safety: :unsafe), covers: ["mutable-constants"] } end |
.constant_values(file) ⇒ Object
85 86 87 88 89 |
# File 'lib/audition/rewriters.rb', line 85 def self.constant_values(file) collector = ConstantValues.new collector.visit(file.root) collector.values end |
.deep_literal?(node, classifier) ⇒ Boolean
Bare literals only: [...].freeze is a method call, and
under the magic comment Ruby raises for it at assignment
because the shallowly-frozen value is unshareable
(verified on 4.0 with jwt's NAMED_CURVES).
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/audition/rewriters.rb', line 54 def self.deep_literal?(node, classifier) case node when Prism::ArrayNode node.elements.all? do |element| deep_literal?(element, classifier) end when Prism::HashNode, Prism::KeywordHashNode node.elements.all? do |element| element.is_a?(Prism::AssocNode) && deep_literal?(element.key, classifier) && deep_literal?(element.value, classifier) end when Prism::CallNode false else %i[shareable mutable_string] .include?(classifier.classify(node)) end end |
.plan(file, findings) ⇒ Object
shareable_constant_value: literal raises at load for
non-literal constant values (verified on 4.0), so it is
only planned when every constant assignment in the file is
a literal all the way down: an array literal holding a
local or a call (Racc-generated parser tables are the
canonical case) becomes an unshareable value that the
magic comment rejects at load time. Otherwise, if the
flagged values are all strings, frozen_string_literal
covers them.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/audition/rewriters.rb', line 26 def self.plan(file, findings) return nil if file.shareable_constants? return nil if findings.none? { |f| f.check == "mutable-constants" } classifier = Static::LiteralClassifier.new( frozen_string_literal: file.frozen_string_literal? ) values = constant_values(file) kinds = values.map { |v| classifier.classify(v) } flagged = kinds.reject do |k| %i[shareable unknown].include?(k) end scv_ok = values.all? do |value| deep_literal?(value, classifier) end if scv_ok comment_plan(file, SCV_LINE) elsif !file.frozen_string_literal? && flagged.all? { |k| k == :mutable_string } comment_plan(file, FSL_LINE) end end |