Class: Solargraph::Source::Chain
- Inherits:
-
Object
- Object
- Solargraph::Source::Chain
- Defined in:
- lib/solargraph/source/chain.rb,
lib/solargraph/source/chain/if.rb,
lib/solargraph/source/chain/or.rb,
lib/solargraph/source/chain/call.rb,
lib/solargraph/source/chain/hash.rb,
lib/solargraph/source/chain/head.rb,
lib/solargraph/source/chain/link.rb,
lib/solargraph/source/chain/array.rb,
lib/solargraph/source/chain/q_call.rb,
lib/solargraph/source/chain/literal.rb,
lib/solargraph/source/chain/z_super.rb,
lib/solargraph/source/chain/constant.rb,
lib/solargraph/source/chain/variable.rb,
lib/solargraph/source/chain/block_symbol.rb,
lib/solargraph/source/chain/block_variable.rb,
lib/solargraph/source/chain/class_variable.rb,
lib/solargraph/source/chain/global_variable.rb,
lib/solargraph/source/chain/instance_variable.rb
Overview
Represents an expression as a single call chain at the parse tree level, made up of constants, variables, and method calls, each represented as a Link object.
Computes Pins and/or ComplexTypes representing the interpreted expression.
Defined Under Namespace
Classes: Array, BlockSymbol, BlockVariable, Call, ClassVariable, Constant, GlobalVariable, Hash, Head, If, InstanceVariable, Link, Literal, Or, QCall, Variable, ZSuper
Constant Summary collapse
- UNDEFINED_CALL =
Chain::Call.new('<undefined>', nil)
- UNDEFINED_CONSTANT =
Chain::Constant.new('<undefined>')
- @@inference_stack =
[]
- @@inference_depth =
0- @@inference_invalidation_key =
nil- @@inference_cache =
{}
Constants included from Logging
Logging::DEFAULT_LOG_LEVEL, Logging::LOG_LEVELS
Instance Attribute Summary collapse
- #links ⇒ ::Array<Source::Chain::Link> readonly
-
#node ⇒ Object
readonly
Returns the value of attribute node.
Instance Method Summary collapse
- #base ⇒ Chain
- #constant? ⇒ Boolean
-
#define(api_map, name_pin, locals) ⇒ ::Array<Pin::Base>
Determine potential Pins returned by this chain of words.
- #defined? ⇒ Boolean
- #desc ⇒ String
-
#infer(api_map, name_pin, locals) ⇒ ComplexType
@sg-ignore.
- #infer_uncached(api_map, name_pin, locals) ⇒ ComplexType, ComplexType::UniqueType
-
#initialize(links, node = nil, splat = false) ⇒ Chain
constructor
A new instance of Chain.
- #literal? ⇒ Boolean
-
#nullable?(api_map) ⇒ Boolean
Whether this chain's inferred type should have nil added to it because a
&.earlier in the chain might have short-circuited evaluation to nil. - #splat? ⇒ Boolean
- #to_s ⇒ Object
- #undefined? ⇒ Boolean
Methods included from Logging
Methods included from Equality
Constructor Details
#initialize(links, node = nil, splat = false) ⇒ Chain
Returns a new instance of Chain.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/solargraph/source/chain.rb', line 54 def initialize links, node = nil, splat = false @links = links.clone @links.push UNDEFINED_CALL if @links.empty? head = true @links.map! do |link| result = (head ? link.clone_head : link.clone_body) head = false result end @node = node @splat = splat end |
Instance Attribute Details
#links ⇒ ::Array<Source::Chain::Link> (readonly)
47 48 49 |
# File 'lib/solargraph/source/chain.rb', line 47 def links @links end |
#node ⇒ Object (readonly)
Returns the value of attribute node.
49 50 51 |
# File 'lib/solargraph/source/chain.rb', line 49 def node @node end |
Instance Method Details
#base ⇒ Chain
68 69 70 71 |
# File 'lib/solargraph/source/chain.rb', line 68 def base # @sg-ignore Need to add nil check here @base ||= Chain.new(links[0..-2]) end |
#constant? ⇒ Boolean
194 195 196 |
# File 'lib/solargraph/source/chain.rb', line 194 def constant? links.last.is_a?(Chain::Constant) end |
#define(api_map, name_pin, locals) ⇒ ::Array<Pin::Base>
Determine potential Pins returned by this chain of words
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/solargraph/source/chain.rb', line 102 def define api_map, name_pin, locals return [] if undefined? # working_pin is the surrounding closure pin for the link # being processed, whose #binder method will provide the LHS / # 'self type' of the next link (same as the #return_type method # --the type of the result so far). # # @todo ProxyType uses 'type' for the binder, but ' working_pin = name_pin # @sg-ignore Need to add nil check here links[0..-2].each do |link| pins = link.resolve(api_map, working_pin, locals) type = infer_from_definitions(pins, working_pin, api_map, locals) if type.undefined? logger.debug do "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" end return [] end # We continue to use the context from the head pin, in case # we need it to, for instance, provide context for a block # evaluation. However, we use the last link's return type # for the binder, as this is chaining off of it, and the # binder is now the lhs of the rhs we are evaluating. working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain) logger.debug do "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" end end links.last.last_context = working_pin links.last.resolve(api_map, working_pin, locals) end |
#defined? ⇒ Boolean
189 190 191 |
# File 'lib/solargraph/source/chain.rb', line 189 def defined? !undefined? end |
#desc ⇒ String
239 240 241 |
# File 'lib/solargraph/source/chain.rb', line 239 def desc links.map(&:desc).to_s end |
#infer(api_map, name_pin, locals) ⇒ ComplexType
@sg-ignore
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/solargraph/source/chain.rb', line 141 def infer api_map, name_pin, locals # includes binder as it is mutable in Pin::Block cache_key = [node, node&.location, links, name_pin&.return_type, name_pin&.binder, locals] if @@inference_invalidation_key == api_map.hash cached = @@inference_cache[cache_key] return cached if cached else @@inference_invalidation_key = api_map.hash @@inference_cache = {} end out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible logger.debug do "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" end @@inference_cache[cache_key] = out end |
#infer_uncached(api_map, name_pin, locals) ⇒ ComplexType, ComplexType::UniqueType
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/solargraph/source/chain.rb', line 162 def infer_uncached api_map, name_pin, locals pins = define(api_map, name_pin, locals) if pins.empty? logger.debug do "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" end return ComplexType::UNDEFINED end type = infer_from_definitions(pins, links.last.last_context, api_map, locals) out = maybe_nil(type, api_map) logger.debug do "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}, " \ "name_pin=#{name_pin}, name_pin.closure=#{name_pin&.closure&.inspect}, " \ "name_pin.binder=#{name_pin&.binder}) => #{out..inspect}" end out end |
#literal? ⇒ Boolean
181 182 183 |
# File 'lib/solargraph/source/chain.rb', line 181 def literal? links.last.is_a?(Chain::Literal) end |
#nullable?(api_map) ⇒ Boolean
Whether this chain's inferred type should have nil added to it
because a &. earlier in the chain might have short-circuited
evaluation to nil.
A &. only makes the immediate call nil-or-normal-result; a
later, non-safe-navigated call in the same chain is still
invoked on whatever that produced. If receiver is nil at that
point, Ruby calls the method on nil itself instead of skipping
it - so nil only continues to flow through subsequent links
that NilClass defines as returning self (e.g., #tap,
#itself). Any other subsequent call resolves to a concrete,
non-nil-including type (e.g. NilClass#== returns Boolean), and
nil no longer needs to be tracked past that point (unless a
later &. reintroduces it).
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/solargraph/source/chain.rb', line 219 def nullable? api_map currently_nullable = false links.each do |link| if link.nullable? currently_nullable = true next end next unless currently_nullable next unless link.is_a?(Chain::Call) pin = api_map.get_method_stack('NilClass', link.word, scope: :instance).first # @sg-ignore Need to add nil check here currently_nullable = pin.nil? || pin.return_type.tag == 'self' end currently_nullable end |
#splat? ⇒ Boolean
198 199 200 |
# File 'lib/solargraph/source/chain.rb', line 198 def splat? @splat end |
#to_s ⇒ Object
243 244 245 |
# File 'lib/solargraph/source/chain.rb', line 243 def to_s desc end |
#undefined? ⇒ Boolean
185 186 187 |
# File 'lib/solargraph/source/chain.rb', line 185 def undefined? links.any?(&:undefined?) end |