Class: Solargraph::Pin::BaseVariable

Inherits:
Base
  • Object
show all
Includes:
ParserGem::NodeMethods
Defined in:
lib/solargraph/pin/base_variable.rb

Constant Summary

Constants included from Logging

Logging::DEFAULT_LOG_LEVEL, Logging::LOG_LEVELS

Instance Attribute Summary collapse

Attributes inherited from Base

#code_object, #combine_priority, #directives, #docstring, #location, #name, #path, #source, #type_location

Attributes included from Common

#context, #location

Instance Method Summary collapse

Methods inherited from Base

#all_location_text, #all_rooted?, #assert_location_provided, #assert_same, #assert_same_array_content, #assert_same_count, #assert_same_macros, #assert_source_provided, #best_location, #choose, #choose_longer, #choose_node, #choose_pin_attr, #choose_pin_attr_with_same_name, #choose_priority, #closure, #combine_directives, #combine_name, #comments, #deprecated?, #desc, #dodgy_return_type_source?, #erase_generics, #filename, #gates, #identity, #infer, #inspect, #macros, #maybe_directives?, #nearly?, #needs_consistent_name?, #prefer_rbs_location, #probed?, #proxied?, #proxy, #rbs_location?, #realize, #resolve_generics, #resolve_generics_from_context, #to_rbs, #to_s, #transform_types

Methods included from Logging

log_level, logger

Methods included from Documenting

#documentation, normalize_indentation, strip_html_comments

Methods included from Conversions

#completion_item, #deprecated?, #detail, #link_documentation, #probed?, #proxied?, #reset_conversions, #resolve_completion_item, #signature_help, #text_documentation

Methods included from Common

#binder, #closure, #closure=, #comments, #name, #namespace, #path, #source

Constructor Details

#initialize(assignment: nil, assignments: [], mass_assignment: nil, presence: nil, return_type: nil, intersection_return_type: nil, exclude_return_type: nil, **splat) ⇒ BaseVariable

Returns a new instance of BaseVariable.

Parameters:

  • return_type (ComplexType, nil) (defaults to: nil)
  • assignment (Parser::AST::Node, nil) (defaults to: nil)

    First assignment that was made to this variable

  • assignments (Array<Parser::AST::Node>) (defaults to: [])

    Possible assignments that may have been made to this variable

  • mass_assignment (::Array(Parser::AST::Node, Integer), nil) (defaults to: nil)
  • assignment (Parser::AST::Node, nil) (defaults to: nil)

    First assignment that was made to this variable

  • assignments (Array<Parser::AST::Node>) (defaults to: [])

    Possible assignments that may have been made to this variable

  • exclude_return_type (ComplexType, nil) (defaults to: nil)

    Ensure any return type returned will never include any of these unique types in the unique types of its complex type.

    Example: If a return type is ‘Float | Integer | nil’ and the exclude_return_type is ‘Integer’, the resulting return type will be ‘Float | nil’ because Integer is excluded.

  • intersection_return_type (ComplexType, nil) (defaults to: nil)

    Ensure each unique return type is compatible with at least one element of this complex type. If a ComplexType used as a return type is an union type - we can return any of these - these are intersection types - everything we return needs to meet at least one of these unique types.

    Example: If a return type is ‘Numeric | nil’ and the intersection_return_type is ‘Float | nil’, the resulting return type will be ‘Float | nil’ because Float is compatible with Numeric and nil is compatible with nil.

  • presence (Range, nil) (defaults to: nil)
  • splat (Hash{Symbol => Object})

See Also:



49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/solargraph/pin/base_variable.rb', line 49

def initialize assignment: nil, assignments: [], mass_assignment: nil,
               presence: nil, return_type: nil,
               intersection_return_type: nil, exclude_return_type: nil,
               **splat
  super(**splat)
  @assignments = (assignment.nil? ? [] : [assignment]) + assignments
  # @type [nil, ::Array(Parser::AST::Node, Integer)]
  @mass_assignment = mass_assignment
  @return_type = return_type
  @intersection_return_type = intersection_return_type
  @exclude_return_type = exclude_return_type
  @presence = presence
end

Instance Attribute Details

#assignmentsArray<Parser::AST::Node> (readonly)

Returns:

  • (Array<Parser::AST::Node>)


10
11
12
# File 'lib/solargraph/pin/base_variable.rb', line 10

def assignments
  @assignments
end

#mass_assignmentObject

Returns the value of attribute mass_assignment.



12
13
14
# File 'lib/solargraph/pin/base_variable.rb', line 12

def mass_assignment
  @mass_assignment
end

#presenceRange?

Returns:



15
16
17
# File 'lib/solargraph/pin/base_variable.rb', line 15

def presence
  @presence
end

Instance Method Details

#==(other) ⇒ Object

Parameters:

  • other (Object)


205
206
207
208
209
# File 'lib/solargraph/pin/base_variable.rb', line 205

def == other
  return false unless super
  # @sg-ignore Should add type check on other
  assignment == other.assignment
end

#assignmentParser::AST::Node?

Returns:

  • (Parser::AST::Node, nil)


113
114
115
# File 'lib/solargraph/pin/base_variable.rb', line 113

def assignment
  @assignment ||= assignments.last
end

#combine_assignments(other) ⇒ ::Array<Parser::AST::Node>

Parameters:

  • other (self)

Returns:

  • (::Array<Parser::AST::Node>)


120
121
122
# File 'lib/solargraph/pin/base_variable.rb', line 120

def combine_assignments other
  (other.assignments + assignments).uniq
end

#combine_closure(other) ⇒ Pin::Closure?

Parameters:

  • other (self)

Returns:



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/solargraph/pin/base_variable.rb', line 254

def combine_closure other
  return closure if closure == other.closure

  # choose first defined, as that establishes the scope of the variable
  if closure.nil? || other.closure.nil?
    Solargraph.assert_or_log(:varible_closure_missing) do
      'One of the local variables being combined is missing a closure: ' \
        "#{inspect} vs #{other.inspect}"
    end
    return closure || other.closure
  end

  # @sg-ignore flow sensitive typing needs to handle attrs
  if closure.location.nil? || other.closure.location.nil?
    # @sg-ignore flow sensitive typing needs to handle attrs
    return closure.location.nil? ? other.closure : closure
  end

  # if filenames are different, this will just pick one
  # @sg-ignore flow sensitive typing needs to handle attrs
  return closure if closure.location <= other.closure.location

  other.closure
end

#combine_mass_assignment(other) ⇒ Array(AST::Node, Integer)?

Parameters:

  • other (self)

Returns:

  • (Array(AST::Node, Integer), nil)


106
107
108
109
110
# File 'lib/solargraph/pin/base_variable.rb', line 106

def combine_mass_assignment other
  # @todo pick first non-nil arbitrarily - we don't yet support
  #   mass assignment merging
  mass_assignment || other.mass_assignment
end

#combine_presence(other) ⇒ Range?

Narrow the presence range to the intersection of both.

Parameters:

  • other (self)

Returns:



245
246
247
248
249
250
# File 'lib/solargraph/pin/base_variable.rb', line 245

def combine_presence other
  return presence || other.presence if presence.nil? || other.presence.nil?

  # @sg-ignore flow sensitive typing needs to handle attrs
  Range.new([presence.start, other.presence.start].max, [presence.ending, other.presence.ending].min)
end

#combine_with(other, attrs = {}) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/solargraph/pin/base_variable.rb', line 85

def combine_with other, attrs = {}
  new_assignments = combine_assignments(other)
  new_attrs = attrs.merge({
                            # default values don't exist in RBS parameters; it just
                            # tells you if the arg is optional or not.  Prefer a
                            # provided value if we have one here since we can't rely on
                            # it from RBS so we can infer from it and typecheck on it.
                            assignment: choose(other, :assignment),
                            assignments: new_assignments,
                            mass_assignment: combine_mass_assignment(other),
                            return_type: combine_return_type(other),
                            intersection_return_type: combine_types(other, :intersection_return_type),
                            exclude_return_type: combine_types(other, :exclude_return_type),
                            presence: combine_presence(other)
                          })
  super(other, new_attrs)
end

#completion_item_kindObject



130
131
132
# File 'lib/solargraph/pin/base_variable.rb', line 130

def completion_item_kind
  Solargraph::LanguageServer::CompletionItemKinds::VARIABLE
end

#downcast(presence:, exclude_return_type: nil, intersection_return_type: nil, source: self.source) ⇒ self

Parameters:

  • presence (Range)
  • exclude_return_type (ComplexType, nil) (defaults to: nil)
  • intersection_return_type (ComplexType, nil) (defaults to: nil)
  • source (::Symbol) (defaults to: self.source)

Returns:

  • (self)


69
70
71
72
73
74
75
76
77
78
# File 'lib/solargraph/pin/base_variable.rb', line 69

def downcast presence:, exclude_return_type: nil, intersection_return_type: nil,
             source: self.source
  result = dup
  result.exclude_return_type = exclude_return_type
  result.intersection_return_type = intersection_return_type
  result.source = source
  result.presence = presence
  result.reset_generated!
  result
end

#inner_descObject



124
125
126
127
128
# File 'lib/solargraph/pin/base_variable.rb', line 124

def inner_desc
  super + ", presence=#{presence.inspect}, assignments=#{assignments}, " \
          "intersection_return_type=#{intersection_return_type&.rooted_tags.inspect}, " \
          "exclude_return_type=#{exclude_return_type&.rooted_tags.inspect}"
end

#nil_assignment?Boolean

Returns:

  • (Boolean)


139
140
141
142
143
# File 'lib/solargraph/pin/base_variable.rb', line 139

def nil_assignment?
  # this will always be false - should it be return_type ==
  #   ComplexType::NIL or somesuch?
  return_type.nil?
end

#presence_certain?Boolean

@sg-ignore need boolish support for ? methods

Returns:

  • (Boolean)


227
228
229
# File 'lib/solargraph/pin/base_variable.rb', line 227

def presence_certain?
  exclude_return_type || intersection_return_type
end

#probe(api_map) ⇒ ComplexType, ComplexType::UniqueType

Parameters:

Returns:



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/solargraph/pin/base_variable.rb', line 177

def probe api_map
  assignment_types = assignments.flat_map { |node| return_types_from_node(node, api_map) }
  type_from_assignment = ComplexType.new(assignment_types.flat_map(&:items).uniq) unless assignment_types.empty?
  return adjust_type api_map, type_from_assignment unless type_from_assignment.nil?

  # @todo should handle merging types from mass assignments as
  #   well so that we can do better flow sensitive typing with
  #   multiple assignments
  unless @mass_assignment.nil?
    mass_node, index = @mass_assignment
    types = return_types_from_node(mass_node, api_map)
    types.map! do |type|
      if type.tuple?
        type.all_params[index]
      elsif ['::Array', '::Set', '::Enumerable'].include?(type.rooted_name)
        type.all_params.first
      end
    end.compact!

    return ComplexType::UNDEFINED if types.empty?

    return adjust_type api_map, ComplexType.new(types.uniq).qualify(api_map, *gates)
  end

  ComplexType::UNDEFINED
end

#reset_generated!Object



80
81
82
83
# File 'lib/solargraph/pin/base_variable.rb', line 80

def reset_generated!
  @assignment = nil
  super
end

#return_typeComplexType?

Returns:



216
217
218
# File 'lib/solargraph/pin/base_variable.rb', line 216

def return_type
  generate_complex_type || @return_type || intersection_return_type || ComplexType::UNDEFINED
end

#return_types_from_node(parent_node, api_map) ⇒ ::Array<ComplexType>

Parameters:

  • parent_node (Parser::AST::Node)
  • api_map (ApiMap)

Returns:



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/solargraph/pin/base_variable.rb', line 152

def return_types_from_node parent_node, api_map
  types = []
  value_position_nodes_only(parent_node).each do |node|
    # Nil nodes may not have a location
    if node.nil? || node.type == :NIL || node.type == :nil
      types.push ComplexType::NIL
    else
      rng = Range.from_node(node)
      next if rng.nil?
      pos = rng.ending
      # @sg-ignore Need to add nil check here
      clip = api_map.clip_at(location.filename, pos)
      # Use the return node for inference. The clip might infer from the
      # first node in a method call instead of the entire call.
      chain = Parser.chain(node, nil, nil)
      # @sg-ignore Need to add nil check here
      result = chain.infer(api_map, closure, clip.locals).self_to_type(closure.context)
      types.push result unless result.undefined?
    end
  end
  types
end

#starts_at?(other_loc) ⇒ Boolean

@sg-ignore flow sensitive typing needs to handle attrs

Parameters:

Returns:

  • (Boolean)


233
234
235
236
237
238
# File 'lib/solargraph/pin/base_variable.rb', line 233

def starts_at? other_loc
  location&.filename == other_loc.filename &&
    presence &&
    # @sg-ignore flow sensitive typing needs to handle attrs
    presence.start == other_loc.range.start
end

#symbol_kindInteger

Returns:

  • (Integer)


135
136
137
# File 'lib/solargraph/pin/base_variable.rb', line 135

def symbol_kind
  Solargraph::LanguageServer::SymbolKinds::VARIABLE
end

#type_descObject



211
212
213
# File 'lib/solargraph/pin/base_variable.rb', line 211

def type_desc
  "#{super} = #{assignment&.type.inspect}"
end

#typify(api_map) ⇒ Object



220
221
222
223
224
# File 'lib/solargraph/pin/base_variable.rb', line 220

def typify api_map
  raw_return_type = super

  adjust_type(api_map, raw_return_type)
end

#variable?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/solargraph/pin/base_variable.rb', line 145

def variable?
  true
end

#visible_at?(other_closure, other_loc) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


281
282
283
284
285
286
287
# File 'lib/solargraph/pin/base_variable.rb', line 281

def visible_at? other_closure, other_loc
  # @sg-ignore flow sensitive typing needs to handle attrs
  location.filename == other_loc.filename &&
    # @sg-ignore flow sensitive typing needs to handle attrs
    (!presence || presence.include?(other_loc.range.start)) &&
    visible_in_closure?(other_closure)
end