Class: Audition::Static::LiteralClassifier

Inherits:
Object
  • Object
show all
Defined in:
lib/audition/static/literal_classifier.rb

Overview

Classifies a Prism expression node by Ractor shareability:

:shareable         proven deeply shareable
:mutable_string    unfrozen String literal
:mutable_container Array/Hash literal or constructor
:mutable_call      unfrozen String or Regexp returned by
                 a call (`.tr`, `format`, `Regexp.new`)
:shallow_freeze    frozen container with mutable elements
:sync_primitive    Mutex/Queue/... constructor
:proc              lambda or proc
:default_proc      Hash.new with a block; the block
                 survives .freeze and stays unshareable
:unknown           cannot tell statically

Constant Summary collapse

SYNC_PRIMITIVES =
%w[
  Mutex Monitor Queue SizedQueue ConditionVariable
  Thread::Mutex Thread::Queue Thread::SizedQueue
  Thread::ConditionVariable
].freeze
SHAREABLE_FACTORIES =
%w[Struct Class Module].freeze
STRING_ONLY_METHODS =

Calls returning a fresh, unfrozen String or Regexp; # frozen_string_literal: true covers literals only. Rails hit both shapes (.tr and Regexp.new) in constants during its ractorization. These names belong to String alone in core, so any receiver qualifies.

%i[
  tr tr_s gsub sub squeeze strip lstrip rstrip chomp chop
  center ljust rjust encode scrub unicode_normalize
].freeze
STRING_LITERAL_METHODS =

Unambiguous only on a String literal receiver: Symbols and numbers define these too and return shareable values.

%i[
  + * % upcase downcase capitalize swapcase reverse dup
  succ next
].freeze
FORMATTERS =
%i[format sprintf].freeze
REGEXP_FACTORIES =
%i[new union compile].freeze

Instance Method Summary collapse

Constructor Details

#initialize(frozen_string_literal:) ⇒ LiteralClassifier

Returns a new instance of LiteralClassifier.

Parameters:

  • frozen_string_literal (Boolean)

    whether the file has the frozen_string_literal magic comment



47
48
49
# File 'lib/audition/static/literal_classifier.rb', line 47

def initialize(frozen_string_literal:)
  @frozen_string_literal = frozen_string_literal
end

Instance Method Details

#classify(node) ⇒ Symbol

Returns classification, see class docs.

Parameters:

  • node (Prism::Node)

    an expression node

Returns:

  • (Symbol)

    classification, see class docs



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/audition/static/literal_classifier.rb', line 53

def classify(node)
  case node
  when Prism::IntegerNode, Prism::FloatNode,
       Prism::RationalNode, Prism::ImaginaryNode,
       Prism::SymbolNode, Prism::InterpolatedSymbolNode,
       Prism::TrueNode, Prism::FalseNode, Prism::NilNode,
       Prism::RegularExpressionNode,
       Prism::InterpolatedRegularExpressionNode
    :shareable
  when Prism::StringNode
    @frozen_string_literal ? :shareable : :mutable_string
  when Prism::InterpolatedStringNode
    classify_interpolated_string(node)
  when Prism::ArrayNode, Prism::HashNode,
       Prism::KeywordHashNode
    container_kind(node)
  when Prism::RangeNode
    ends = [node.left, node.right].compact
    if ends.all? { |n| classify(n) == :shareable }
      :shareable
    else
      :unknown
    end
  when Prism::LambdaNode
    :proc
  when Prism::CallNode
    classify_call(node)
  when Prism::IfNode
    ternary_kind(node)
  else
    :unknown
  end
end

#const_name(node) ⇒ Object

::Mutex and Mutex are the same constant for matching purposes; the leading colons are stripped.



89
90
91
92
93
94
95
96
# File 'lib/audition/static/literal_classifier.rb', line 89

def const_name(node)
  case node
  when Prism::ConstantReadNode
    node.name.to_s
  when Prism::ConstantPathNode
    node.location.slice.delete_prefix("::")
  end
end