steep 1.9.0.dev.2 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -0
  3. data/README.md +9 -4
  4. data/Rakefile +1 -0
  5. data/Steepfile +11 -0
  6. data/bin/generate-diagnostics-docs.rb +112 -0
  7. data/lib/steep/ast/builtin.rb +1 -0
  8. data/lib/steep/ast/ignore.rb +1 -1
  9. data/lib/steep/ast/types/factory.rb +2 -0
  10. data/lib/steep/cli.rb +9 -2
  11. data/lib/steep/diagnostic/lsp_formatter.rb +8 -1
  12. data/lib/steep/diagnostic/ruby.rb +65 -3
  13. data/lib/steep/diagnostic/signature.rb +4 -4
  14. data/lib/steep/drivers/check.rb +3 -3
  15. data/lib/steep/drivers/diagnostic_printer.rb +1 -1
  16. data/lib/steep/drivers/init.rb +6 -3
  17. data/lib/steep/expectations.rb +1 -1
  18. data/lib/steep/interface/builder.rb +2 -3
  19. data/lib/steep/interface/function.rb +13 -0
  20. data/lib/steep/interface/method_type.rb +5 -0
  21. data/lib/steep/interface/shape.rb +1 -1
  22. data/lib/steep/project/dsl.rb +2 -0
  23. data/lib/steep/server/change_buffer.rb +1 -1
  24. data/lib/steep/server/interaction_worker.rb +5 -5
  25. data/lib/steep/server/master.rb +2 -17
  26. data/lib/steep/server/type_check_controller.rb +2 -2
  27. data/lib/steep/server/type_check_worker.rb +1 -1
  28. data/lib/steep/services/completion_provider.rb +4 -4
  29. data/lib/steep/services/goto_service.rb +3 -3
  30. data/lib/steep/services/hover_provider/rbs.rb +1 -1
  31. data/lib/steep/services/hover_provider/ruby.rb +6 -6
  32. data/lib/steep/services/signature_help_provider.rb +8 -8
  33. data/lib/steep/services/type_check_service.rb +8 -8
  34. data/lib/steep/signature/validator.rb +3 -3
  35. data/lib/steep/source.rb +4 -4
  36. data/lib/steep/subtyping/check.rb +3 -3
  37. data/lib/steep/subtyping/constraints.rb +4 -4
  38. data/lib/steep/type_construction.rb +84 -45
  39. data/lib/steep/type_inference/block_params.rb +3 -3
  40. data/lib/steep/type_inference/context.rb +1 -1
  41. data/lib/steep/type_inference/method_params.rb +1 -1
  42. data/lib/steep/type_inference/type_env.rb +3 -3
  43. data/lib/steep/version.rb +1 -1
  44. data/manual/annotations.md +37 -0
  45. data/manual/ignore.md +20 -0
  46. data/manual/ruby-diagnostics.md +1812 -0
  47. data/steep.gemspec +1 -1
  48. metadata +8 -5
@@ -164,6 +164,7 @@ module Steep
164
164
  end
165
165
 
166
166
  def group(name, &block)
167
+ name = name.to_str.to_sym unless Symbol === name
167
168
  group = GroupDSL.new(name, self)
168
169
 
169
170
  Steep.logger.tagged "group=#{name}" do
@@ -230,6 +231,7 @@ module Steep
230
231
  end
231
232
 
232
233
  def target(name, &block)
234
+ name = name.to_str.to_sym unless Symbol === name
233
235
  dsl = TargetDSL.new(name, project: project)
234
236
 
235
237
  Steep.logger.tagged "target=#{name}" do
@@ -46,7 +46,7 @@ module Steep
46
46
 
47
47
  changes[path] ||= []
48
48
  request[:params][:contentChanges].each do |change|
49
- changes[path] << Services::ContentChange.new(
49
+ changes.fetch(path) << Services::ContentChange.new(
50
50
  range: change[:range]&.yield_self {|range|
51
51
  [
52
52
  range[:start].yield_self {|pos| Services::ContentChange::Position.new(line: pos[:line] + 1, column: pos[:character]) },
@@ -156,7 +156,7 @@ module Steep
156
156
  case
157
157
  when target = project.target_for_source_path(job.path)
158
158
  file = service.source_files[job.path] or return
159
- subtyping = service.signature_services[target.name].current_subtyping or return
159
+ subtyping = service.signature_services.fetch(target.name).current_subtyping or return
160
160
 
161
161
  provider = Services::CompletionProvider.new(source_text: file.content, path: job.path, subtyping: subtyping)
162
162
  items = begin
@@ -184,12 +184,12 @@ module Steep
184
184
  case sig_service.status
185
185
  when Services::SignatureService::SyntaxErrorStatus, Services::SignatureService::AncestorErrorStatus
186
186
  if buffer = sig_service.latest_env.buffers.find {|buf| Pathname(buf.name) == Pathname(relative_path) }
187
- dirs = sig_service.latest_env.signatures[buffer][0]
187
+ dirs = sig_service.latest_env.signatures.fetch(buffer)[0]
188
188
  else
189
189
  dirs = [] #: Array[RBS::AST::Directives::t]
190
190
  end
191
191
  else
192
- signature = sig_service.files[relative_path].signature
192
+ signature = sig_service.files.fetch(relative_path).signature
193
193
  signature.is_a?(Array) or raise
194
194
  buffer, dirs, decls = signature
195
195
 
@@ -210,7 +210,7 @@ module Steep
210
210
  end
211
211
  end
212
212
 
213
- buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files[relative_path].content)
213
+ buffer = RBS::Buffer.new(name: relative_path, content: sig_service.files.fetch(relative_path).content)
214
214
  prefix = Services::TypeNameCompletion::Prefix.parse(buffer, line: job.line, column: job.column)
215
215
 
216
216
  completion = Services::TypeNameCompletion.new(env: sig_service.latest_env, context: context, dirs: dirs)
@@ -451,7 +451,7 @@ module Steep
451
451
  Steep.logger.tagged("##{__method__}") do
452
452
  if target = project.target_for_source_path(job.path)
453
453
  file = service.source_files[job.path] or return
454
- subtyping = service.signature_services[target.name].current_subtyping or return
454
+ subtyping = service.signature_services.fetch(target.name).current_subtyping or return
455
455
  source =
456
456
  Source.parse(file.content, path: file.path, factory: subtyping.factory)
457
457
  .without_unrelated_defs(line: job.line, column: job.column)
@@ -178,7 +178,6 @@ module Steep
178
178
  attr_reader :job_queue, :write_queue
179
179
 
180
180
  attr_reader :current_type_check_request
181
- attr_reader :current_diagnostics
182
181
  attr_reader :controller
183
182
  attr_reader :result_controller
184
183
 
@@ -197,7 +196,6 @@ module Steep
197
196
  @commandline_args = []
198
197
  @job_queue = queue
199
198
  @write_queue = SizedQueue.new(100)
200
- @current_diagnostics = {}
201
199
 
202
200
  @controller = TypeCheckController.new(project: project)
203
201
  @result_controller = ResultController.new()
@@ -802,15 +800,7 @@ module Steep
802
800
  Steep.logger.info "Starting new progress..."
803
801
 
804
802
  @current_type_check_request = request
805
- if last_request
806
- checking_paths = request.each_path.to_set
807
- current_diagnostics.keep_if do |path, _|
808
- checking_paths.include?(path)
809
- end
810
- else
811
- current_diagnostics.clear
812
- end
813
-
803
+
814
804
  if progress
815
805
  # If `request:` keyword arg is not given
816
806
  request.work_done_progress.begin("Type checking", request_id: fresh_request_id)
@@ -909,15 +899,10 @@ module Steep
909
899
 
910
900
  def push_diagnostics(path, diagnostics)
911
901
  if diagnostics
912
- ds = (current_diagnostics[path] ||= [])
913
-
914
- ds.concat(diagnostics)
915
- ds.uniq!
916
-
917
902
  write_queue.push SendMessageJob.to_client(
918
903
  message: {
919
904
  method: :"textDocument/publishDiagnostics",
920
- params: { uri: Steep::PathHelper.to_uri(path).to_s, diagnostics: ds }
905
+ params: { uri: Steep::PathHelper.to_uri(path).to_s, diagnostics: diagnostics }
921
906
  }
922
907
  )
923
908
  end
@@ -266,7 +266,7 @@ module Steep
266
266
  request.code_paths << [target_group.name, path]
267
267
  end
268
268
  else
269
- group_set = groups.map do |group_name|
269
+ group_set = groups.filter_map do |group_name|
270
270
  target_name, group_name = group_name.split(".", 2)
271
271
  target_name or raise
272
272
 
@@ -280,7 +280,7 @@ module Steep
280
280
  else
281
281
  project.targets.find {|target| target.name == target_name }
282
282
  end
283
- end.compact.to_set
283
+ end.to_set
284
284
 
285
285
  files.signature_paths.each do |path, target_group|
286
286
  if group_set.include?(target_group)
@@ -223,7 +223,7 @@ module Steep
223
223
  Steep.measure "Generating workspace symbol list for query=`#{query}`" do
224
224
  provider = Index::SignatureSymbolProvider.new(project: project, assignment: assignment)
225
225
  project.targets.each do |target|
226
- index = service.signature_services[target.name].latest_rbs_index
226
+ index = service.signature_services.fetch(target.name).latest_rbs_index
227
227
  provider.indexes[target] = index
228
228
  end
229
229
 
@@ -105,9 +105,9 @@ module Steep
105
105
  def decl
106
106
  case
107
107
  when absolute_type_name.interface?
108
- env.interface_decls[absolute_type_name].decl
108
+ env.interface_decls.fetch(absolute_type_name).decl
109
109
  when absolute_type_name.alias?
110
- env.type_alias_decls[absolute_type_name].decl
110
+ env.type_alias_decls.fetch(absolute_type_name).decl
111
111
  when absolute_type_name.class?
112
112
  case entry = env.module_class_entry(absolute_type_name)
113
113
  when RBS::Environment::ClassEntry, RBS::Environment::ModuleEntry
@@ -127,11 +127,11 @@ module Steep
127
127
 
128
128
  case
129
129
  when absolute_type_name.interface?
130
- if comment = env.interface_decls[absolute_type_name].decl.comment
130
+ if comment = env.interface_decls.fetch(absolute_type_name).decl.comment
131
131
  comments << comment
132
132
  end
133
133
  when absolute_type_name.alias?
134
- if comment = env.type_alias_decls[absolute_type_name].decl.comment
134
+ if comment = env.type_alias_decls.fetch(absolute_type_name).decl.comment
135
135
  comments << comment
136
136
  end
137
137
  when absolute_type_name.class?
@@ -97,7 +97,7 @@ module Steep
97
97
  relative_path = project.relative_path(path)
98
98
 
99
99
  target = type_check.project.target_for_path(relative_path) or return []
100
- source = type_check.source_files[relative_path]
100
+ source = type_check.source_files.fetch(relative_path)
101
101
  typing, signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
102
102
 
103
103
  typing or return []
@@ -163,7 +163,7 @@ module Steep
163
163
 
164
164
  case
165
165
  when target = type_check.project.target_for_source_path(relative_path)
166
- source = type_check.source_files[relative_path] or return []
166
+ source = type_check.source_files.fetch(relative_path, nil) or return []
167
167
  typing, _signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
168
168
  if typing
169
169
  node, *parents = typing.source.find_nodes(line: line, column: column)
@@ -284,7 +284,7 @@ module Steep
284
284
  end
285
285
 
286
286
  def type_check_path(target:, path:, content:, line:, column:)
287
- signature_service = type_check.signature_services[target.name]
287
+ signature_service = type_check.signature_services.fetch(target.name)
288
288
  subtyping = signature_service.current_subtyping or return
289
289
  source = Source.parse(content, path: path, factory: subtyping.factory)
290
290
  source = source.without_unrelated_defs(line: line, column: column)
@@ -17,7 +17,7 @@ module Steep
17
17
  end
18
18
 
19
19
  def content_for(target:, path:, line:, column:)
20
- service = self.service.signature_services[target.name]
20
+ service = self.service.signature_services.fetch(target.name)
21
21
 
22
22
  env = service.latest_env
23
23
  buffer = env.buffers.find {|buf| buf.name.to_s == path.to_s } or return
@@ -65,14 +65,14 @@ module Steep
65
65
  def method_definition_for(factory, type_name, singleton_method: nil, instance_method: nil)
66
66
  case
67
67
  when instance_method
68
- factory.definition_builder.build_instance(type_name).methods[instance_method]
68
+ factory.definition_builder.build_instance(type_name).methods.fetch(instance_method)
69
69
  when singleton_method
70
70
  methods = factory.definition_builder.build_singleton(type_name).methods
71
71
 
72
72
  if singleton_method == :new
73
- methods[:new] || methods[:initialize]
73
+ methods[:new] || methods.fetch(:initialize)
74
74
  else
75
- methods[singleton_method]
75
+ methods.fetch(singleton_method)
76
76
  end
77
77
  else
78
78
  raise "One of the instance_method or singleton_method is required"
@@ -80,7 +80,7 @@ module Steep
80
80
  end
81
81
 
82
82
  def typecheck(target, path:, content:, line:, column:)
83
- subtyping = service.signature_services[target.name].current_subtyping or return
83
+ subtyping = service.signature_services.fetch(target.name).current_subtyping or return
84
84
  source = Source.parse(content, path: path, factory: subtyping.factory)
85
85
  source = source.without_unrelated_defs(line: line, column: column)
86
86
  resolver = ::RBS::Resolver::ConstantResolver.new(builder: subtyping.factory.definition_builder)
@@ -145,8 +145,8 @@ module Steep
145
145
  result_node =
146
146
  case parents[0]&.type
147
147
  when :block, :numblock
148
- if node == parents[0].children[0]
149
- parents[0]
148
+ if node == parents.fetch(0).children[0]
149
+ parents.fetch(0)
150
150
  else
151
151
  node
152
152
  end
@@ -145,13 +145,13 @@ module Steep
145
145
  # Cursor is not on the argument (maybe on comma after argument)
146
146
  return 0 if last_argument_nodes.nil? # No arguments
147
147
 
148
- case last_argument_nodes[-2].type
148
+ case last_argument_nodes.fetch(-2).type
149
149
  when :splat
150
150
  method_type.type.required_positionals.size + method_type.type.optional_positionals.size + 1 if method_type.type.rest_positionals
151
151
  when :kwargs
152
- case last_argument_nodes[-3].type
152
+ case last_argument_nodes.fetch(-3).type
153
153
  when :pair
154
- argname = last_argument_nodes[-3].children.first.children.first
154
+ argname = last_argument_nodes.fetch(-3).children.first.children.first
155
155
  if method_type.type.required_keywords.key?(argname)
156
156
  positionals + method_type.type.required_keywords.keys.index(argname).to_i + 1
157
157
  elsif method_type.type.optional_keywords.key?(argname)
@@ -163,7 +163,7 @@ module Steep
163
163
  positionals + method_type.type.required_keywords.size + method_type.type.optional_keywords.size if method_type.type.rest_keywords
164
164
  end
165
165
  else
166
- pos = (node.children[2...] || raise).index { |c| c.location == last_argument_nodes[-2].location }.to_i
166
+ pos = (node.children[2...] || raise).index { |c| c.location == last_argument_nodes.fetch(-2).location }.to_i
167
167
  if method_type.type.rest_positionals
168
168
  [pos + 1, positionals - 1].min
169
169
  else
@@ -172,14 +172,14 @@ module Steep
172
172
  end
173
173
  else
174
174
  # Cursor is on the argument
175
- case argument_nodes[-2].type
175
+ case argument_nodes.fetch(-2).type
176
176
  when :splat
177
177
  method_type.type.required_positionals.size + method_type.type.optional_positionals.size if method_type.type.rest_positionals
178
178
  when :kwargs
179
179
  if argument_nodes[-3]
180
- case argument_nodes[-3].type
180
+ case argument_nodes.fetch(-3).type
181
181
  when :pair
182
- argname = argument_nodes[-3].children.first.children.first
182
+ argname = argument_nodes.fetch(-3).children.first.children.first
183
183
  if method_type.type.required_keywords.key?(argname)
184
184
  positionals + method_type.type.required_keywords.keys.index(argname).to_i
185
185
  elsif method_type.type.optional_keywords.key?(argname)
@@ -192,7 +192,7 @@ module Steep
192
192
  end
193
193
  end
194
194
  else
195
- pos = (node.children[2...] || raise).index { |c| c.location == argument_nodes[-2].location }.to_i
195
+ pos = (node.children[2...] || raise).index { |c| c.location == argument_nodes.fetch(-2).location }.to_i
196
196
  [pos, positionals - 1].min
197
197
  end
198
198
  end
@@ -94,7 +94,7 @@ module Steep
94
94
  signature_diagnostics = {}
95
95
 
96
96
  project.targets.each do |target|
97
- service = signature_services[target.name]
97
+ service = signature_services.fetch(target.name)
98
98
 
99
99
  service.each_rbs_path do |path|
100
100
  signature_diagnostics[path] ||= []
@@ -105,13 +105,13 @@ module Steep
105
105
  service.status.diagnostics.group_by {|diag| diag.location&.buffer&.name&.to_s }.each do |path_string, diagnostics|
106
106
  if path_string
107
107
  path = Pathname(path_string)
108
- signature_diagnostics[path].push(*diagnostics)
108
+ signature_diagnostics.fetch(path).push(*diagnostics)
109
109
  end
110
110
  end
111
111
  when SignatureService::LoadedStatus
112
112
  validation_diagnostics = signature_validation_diagnostics[target.name] || {}
113
113
  validation_diagnostics.each do |path, diagnostics|
114
- signature_diagnostics[path].push(*diagnostics)
114
+ signature_diagnostics.fetch(path).push(*diagnostics)
115
115
  end
116
116
  end
117
117
  end
@@ -154,7 +154,7 @@ module Steep
154
154
  def validate_signature(path:, target:)
155
155
  Steep.logger.tagged "#validate_signature(path=#{path})" do
156
156
  Steep.measure "validation" do
157
- service = signature_services[target.name]
157
+ service = signature_services.fetch(target.name)
158
158
 
159
159
  raise "#{path} is not library nor signature of #{target.name}" unless target.possible_signature_file?(path) || service.env_rbs_paths.include?(path)
160
160
 
@@ -221,7 +221,7 @@ module Steep
221
221
  end
222
222
  end
223
223
 
224
- signature_validation_diagnostics[target.name][path] = diagnostics
224
+ signature_validation_diagnostics.fetch(target.name)[path] = diagnostics
225
225
  end
226
226
  end
227
227
  end
@@ -231,11 +231,11 @@ module Steep
231
231
 
232
232
  Steep.logger.tagged "#typecheck_source(path=#{path})" do
233
233
  Steep.measure "typecheck" do
234
- signature_service = signature_services[target.name]
234
+ signature_service = signature_services.fetch(target.name)
235
235
  subtyping = signature_service.current_subtyping
236
236
 
237
237
  if subtyping
238
- text = source_files[path].content
238
+ text = source_files.fetch(path).content
239
239
  file = type_check_file(target: target, subtyping: subtyping, path: path, text: text) { signature_service.latest_constant_resolver }
240
240
  source_files[path] = file
241
241
 
@@ -287,7 +287,7 @@ module Steep
287
287
  SourceFile.with_typing(path: path, content: text, node: source.node, typing: typing, ignores: ignores)
288
288
  end
289
289
  rescue AnnotationParser::SyntaxError => exn
290
- error = Diagnostic::Ruby::SyntaxError.new(message: exn.message, location: exn.location)
290
+ error = Diagnostic::Ruby::AnnotationSyntaxError.new(message: exn.message, location: exn.location)
291
291
  SourceFile.with_syntax_error(path: path, content: text, error: error)
292
292
  rescue ::Parser::SyntaxError => exn
293
293
  error = Diagnostic::Ruby::SyntaxError.new(message: exn.message, location: (_ = exn).diagnostic.location)
@@ -130,7 +130,7 @@ module Steep
130
130
  ]
131
131
  when RBS::Types::Alias
132
132
  type_name = env.normalize_type_name?(type.name) or return
133
- entry = env.type_alias_decls[type_name]
133
+ entry = env.type_alias_decls.fetch(type_name)
134
134
 
135
135
  [
136
136
  type_name,
@@ -485,7 +485,7 @@ module Steep
485
485
  location =
486
486
  case ancestor.source
487
487
  when :super
488
- primary_decl = env.class_decls[name].primary.decl
488
+ primary_decl = env.class_decls.fetch(name).primary.decl
489
489
  primary_decl.is_a?(RBS::AST::Declarations::Class) or raise
490
490
  if super_class = primary_decl.super_class
491
491
  super_class.location
@@ -594,7 +594,7 @@ module Steep
594
594
  end
595
595
  end
596
596
 
597
- def validate_one_alias(name, entry = env.type_alias_decls[name])
597
+ def validate_one_alias(name, entry = env.type_alias_decls.fetch(name))
598
598
  *, inner_most_outer_module = entry.outer
599
599
  if inner_most_outer_module
600
600
  class_type = AST::Types::Name::Singleton.new(name: inner_most_outer_module.name)
data/lib/steep/source.rb CHANGED
@@ -88,7 +88,7 @@ module Steep
88
88
 
89
89
  annotations.each do |annot|
90
90
  map[node] ||= []
91
- map[node] << annot
91
+ map.fetch(node) << annot
92
92
  end
93
93
 
94
94
  ignores = comments.filter_map do |comment|
@@ -269,7 +269,7 @@ module Steep
269
269
 
270
270
  associated_annotations.each do |annot|
271
271
  mapping[node] ||= []
272
- mapping[node] << annot
272
+ mapping.fetch(node) << annot
273
273
  end
274
274
 
275
275
  annotations.replace(other_annotations)
@@ -379,7 +379,7 @@ module Steep
379
379
  position = buffer.loc_to_pos([line, column])
380
380
 
381
381
  if heredoc_nodes = find_heredoc_nodes(line, column, position)
382
- Source.each_child_node(heredoc_nodes[0]) do |child|
382
+ Source.each_child_node(heredoc_nodes.fetch(0)) do |child|
383
383
  if nodes = find_nodes_loc(child, position, heredoc_nodes)
384
384
  return nodes
385
385
  end
@@ -442,7 +442,7 @@ module Steep
442
442
 
443
443
  annotations.each do |annot|
444
444
  mapping[node_] ||= []
445
- mapping[node_] << annot
445
+ mapping.fetch(node_) << annot
446
446
  end
447
447
 
448
448
  Source.new(buffer: buffer, path: path, node: node_, mapping: mapping, comments: comments, ignores: ignores)
@@ -515,7 +515,7 @@ module Steep
515
515
  types: relation.sub_type.types
516
516
  )
517
517
 
518
- check_type(Relation.new(sub_type: tuple_element_type, super_type: super_type.args[0]))
518
+ check_type(Relation.new(sub_type: tuple_element_type, super_type: super_type.args.fetch(0)))
519
519
  end
520
520
 
521
521
  when relation.sub_type.is_a?(AST::Types::Tuple)
@@ -1057,10 +1057,10 @@ module Steep
1057
1057
 
1058
1058
  sup_flat_kws.each do |name, _|
1059
1059
  if sub_flat_kws.key?(name)
1060
- pairs << [sub_flat_kws[name], sup_flat_kws[name]]
1060
+ pairs << [sub_flat_kws.fetch(name), sup_flat_kws.fetch(name)]
1061
1061
  else
1062
1062
  if sub_params.rest_keywords
1063
- pairs << [sub_params.rest_keywords, sup_flat_kws[name]]
1063
+ pairs << [sub_params.rest_keywords, sup_flat_kws.fetch(name)]
1064
1064
  else
1065
1065
  return failure
1066
1066
  end
@@ -102,7 +102,7 @@ module Steep
102
102
  end
103
103
 
104
104
  def add(var, sub_type: nil, super_type: nil, skip: false)
105
- subs, supers, skips = dictionary[var]
105
+ subs, supers, skips = dictionary.fetch(var)
106
106
 
107
107
  if sub_type.is_a?(AST::Types::Logic::Base)
108
108
  sub_type = AST::Builtin.bool_type
@@ -204,7 +204,7 @@ module Steep
204
204
  if skip
205
205
  upper_bound = upper_bound_types(var)
206
206
  else
207
- _, upper_bound, _ = dictionary[var]
207
+ _, upper_bound, _ = dictionary.fetch(var)
208
208
  end
209
209
 
210
210
  case upper_bound.size
@@ -320,12 +320,12 @@ module Steep
320
320
  end
321
321
 
322
322
  def lower_bound_types(var_name)
323
- lower, _, _ = dictionary[var_name]
323
+ lower, _, _ = dictionary.fetch(var_name)
324
324
  lower
325
325
  end
326
326
 
327
327
  def upper_bound_types(var_name)
328
- _, upper, skips = dictionary[var_name]
328
+ _, upper, skips = dictionary.fetch(var_name)
329
329
 
330
330
  case
331
331
  when upper.empty?