steep 0.43.1 → 0.44.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7af0aaa9c4c7b0dd2bae9104603131370d976133d866e3d7760c4b2fa581e694
4
- data.tar.gz: eb1421e3b07c1f06e427073bb72d55138eda935e2269874f5727eecb035a00fb
3
+ metadata.gz: 9f5bbaa95e53fda280f5b1c239c0c72093d51cbb6ca0699dbc5c580bcd857989
4
+ data.tar.gz: c9d4c6e70696e7f85d17b872ca6cec7088f9ee44d9768e9341564fc48e0b8f65
5
5
  SHA512:
6
- metadata.gz: 815776632903ca2679af4ae7e7b4a22fa4388956b56706b862f21d76bea6edbbb928d8423504a99a352db71bf7d777e1148a57fe803b582814f1f1526cc45358
7
- data.tar.gz: 70839258fe699f8b246b66cf5e865cb33a9231b5d34e12b40f880bc6b3c50566fe53e0df74645785a3bf1091cbbe34ad4e216ac9d37fdcc3bb3ef32532ab5363
6
+ metadata.gz: 21fb7999c14382b1e6f3d2ad6dba6ec1db2c9169c4620ee66c76ecff8bee8d198b4f972e2afc181405ba8f8f378ae16d07c070a3886135e92e3d788257c93d94
7
+ data.tar.gz: e06a0f4d2d5e726a749c79c8828d86f0de2c5494126d208ddaddc320fa7911b89eb5e051a231db0c97078d0f844b35a04552d47cbd3c8fe001c7213bea90f0ac
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.44.0 (2021-04-22)
6
+
7
+ * Implement LSP go to definition/implementation ([#371](https://github.com/soutaro/steep/pull/371), [#375](https://github.com/soutaro/steep/pull/375))
8
+ * Fix typing on passing optional block ([#373](https://github.com/soutaro/steep/pull/373))
9
+ * Do not crash when completion request `context` is missing ([#370](https://github.com/soutaro/steep/pull/370))
10
+ * Update RBS ([#376](https://github.com/soutaro/steep/pull/376))
11
+
5
12
  ## 0.43.1 (2021-04-01)
6
13
 
7
14
  * Fix LSP `textDocument/didSave` notification handling ([#368](https://github.com/soutaro/steep/issues/368))
data/Gemfile CHANGED
@@ -12,3 +12,4 @@ gem "racc", "~> 1.4"
12
12
  gem "minitest-reporters"
13
13
  gem "minitest-hooks"
14
14
  gem "stackprof"
15
+ gem "rbs", git: "https://github.com/ruby/rbs.git"
data/bin/output_test.rb CHANGED
@@ -16,6 +16,8 @@ end
16
16
 
17
17
  failed_tests = []
18
18
 
19
+ ALLOW_FAILURE = ["diagnostics-ruby-unsat"]
20
+
19
21
  test_dirs.each do |dir|
20
22
  puts "Running test #{dir}..."
21
23
 
@@ -30,8 +32,12 @@ test_dirs.each do |dir|
30
32
  output, status = Open3.capture2(*command, chdir: dir.to_s)
31
33
 
32
34
  unless status.success?
33
- failed_tests << dir.basename
34
- puts " Failed! 🤕"
35
+ unless ALLOW_FAILURE.include?(dir.basename.to_s)
36
+ failed_tests << dir.basename
37
+ puts " Failed! 🤕"
38
+ else
39
+ puts " Failed! 🤕 (ALLOW_FAILURE)"
40
+ end
35
41
  else
36
42
  puts " Succeed! 👍"
37
43
  end
data/lib/steep.rb CHANGED
@@ -104,6 +104,7 @@ require "steep/services/hover_content"
104
104
  require "steep/services/completion_provider"
105
105
  require "steep/services/stats_calculator"
106
106
  require "steep/services/file_loader"
107
+ require "steep/services/goto_service"
107
108
 
108
109
  require "steep/project"
109
110
  require "steep/project/pattern"
@@ -43,8 +43,51 @@ module Steep
43
43
  end
44
44
  end
45
45
 
46
+ class MethodEntry
47
+ attr_reader :name
48
+
49
+ attr_reader :definitions
50
+ attr_reader :references
51
+
52
+ def initialize(name:)
53
+ @name = name
54
+
55
+ @definitions = Set[].compare_by_identity
56
+ @references = Set[].compare_by_identity
57
+ end
58
+
59
+ def add_definition(node)
60
+ case node.type
61
+ when :def, :defs
62
+ @definitions << node
63
+ else
64
+ raise "Unexpected method definition: #{node.type}"
65
+ end
66
+
67
+ self
68
+ end
69
+
70
+ def add_reference(node)
71
+ case node.type
72
+ when :send, :block
73
+ @references << node
74
+ else
75
+ raise "Unexpected method reference: #{node.type}"
76
+ end
77
+
78
+ self
79
+ end
80
+
81
+ def merge!(other)
82
+ definitions.merge(other.definitions)
83
+ references.merge(other.references)
84
+ self
85
+ end
86
+ end
87
+
46
88
  attr_reader :source
47
89
  attr_reader :constant_index
90
+ attr_reader :method_index
48
91
 
49
92
  attr_reader :parent
50
93
  attr_reader :count
@@ -58,6 +101,7 @@ module Steep
58
101
  @count = @parent_count || 0
59
102
 
60
103
  @constant_index = {}
104
+ @method_index = {}
61
105
  end
62
106
 
63
107
  def new_child
@@ -72,25 +116,31 @@ module Steep
72
116
  entry.merge!(child_entry)
73
117
  end
74
118
 
119
+ method_index.merge!(child.method_index) do |_, entry, child_entry|
120
+ entry.merge!(child_entry)
121
+ end
122
+
75
123
  @count = child.count + 1
76
124
  end
77
125
 
78
- def add_definition(constant:, definition:)
126
+ def add_definition(constant: nil, method: nil, definition:)
79
127
  @count += 1
80
- entry(constant: constant).add_definition(definition)
128
+ entry(constant: constant, method: method).add_definition(definition)
81
129
  self
82
130
  end
83
131
 
84
- def add_reference(constant:, ref:)
132
+ def add_reference(constant: nil, method: nil, ref:)
85
133
  @count += 1
86
- entry(constant: constant).add_reference(ref)
134
+ entry(constant: constant, method: method).add_reference(ref)
87
135
  self
88
136
  end
89
137
 
90
- def entry(constant:)
138
+ def entry(constant: nil, method: nil)
91
139
  case
92
140
  when constant
93
141
  constant_index[constant] ||= ConstantEntry.new(name: constant)
142
+ when method
143
+ method_index[method] ||= MethodEntry.new(name: method)
94
144
  else
95
145
  raise
96
146
  end
@@ -13,6 +13,10 @@ module Steep
13
13
  @optional
14
14
  end
15
15
 
16
+ def required?
17
+ !optional?
18
+ end
19
+
16
20
  def to_optional
17
21
  self.class.new(
18
22
  type: type,
@@ -65,7 +65,7 @@ module Steep
65
65
  uri = URI.parse(params[:textDocument][:uri])
66
66
  path = project.relative_path(Pathname(uri.path))
67
67
  line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
68
- trigger = params[:context][:triggerCharacter]
68
+ trigger = params.dig(:context, :triggerCharacter)
69
69
 
70
70
  queue << CompletionJob.new(id: id, path: path, line: line, column: column, trigger: trigger)
71
71
  end
@@ -499,7 +499,11 @@ module Steep
499
499
  trigger_characters: [".", "@"],
500
500
  work_done_progress: true
501
501
  ),
502
- workspace_symbol_provider: true
502
+ workspace_symbol_provider: true,
503
+ definition_provider: true,
504
+ declaration_provider: true,
505
+ implementation_provider: true,
506
+ type_definition_provider: true
503
507
  )
504
508
  )
505
509
  }
@@ -584,6 +588,23 @@ module Steep
584
588
  end
585
589
  end
586
590
 
591
+ when "textDocument/definition", "textDocument/implementation"
592
+ result_controller << group_request do |group|
593
+ typecheck_workers.each do |worker|
594
+ group << send_request(method: message[:method], params: message[:params], worker: worker)
595
+ end
596
+
597
+ group.on_completion do |handlers|
598
+ links = handlers.flat_map(&:result)
599
+ job_queue << SendMessageJob.to_client(
600
+ message: {
601
+ id: message[:id],
602
+ result: links
603
+ }
604
+ )
605
+ end
606
+ end
607
+
587
608
  when "$/typecheck"
588
609
  request = controller.make_request(
589
610
  guid: message[:params][:guid],
@@ -11,6 +11,31 @@ module Steep
11
11
  TypeCheckCodeJob = Struct.new(:guid, :path, keyword_init: true)
12
12
  ValidateAppSignatureJob = Struct.new(:guid, :path, keyword_init: true)
13
13
  ValidateLibrarySignatureJob = Struct.new(:guid, :path, keyword_init: true)
14
+ GotoJob = Struct.new(:id, :kind, :params, keyword_init: true) do
15
+ def self.implementation(id:, params:)
16
+ new(
17
+ kind: :implementation,
18
+ id: id,
19
+ params: params
20
+ )
21
+ end
22
+
23
+ def self.definition(id:, params:)
24
+ new(
25
+ kind: :definition,
26
+ id: id,
27
+ params: params
28
+ )
29
+ end
30
+
31
+ def implementation?
32
+ kind == :implementation
33
+ end
34
+
35
+ def definition?
36
+ kind == :definition
37
+ end
38
+ end
14
39
 
15
40
  include ChangeBuffer
16
41
 
@@ -44,6 +69,10 @@ module Steep
44
69
  when "$/typecheck/start"
45
70
  params = request[:params]
46
71
  enqueue_typecheck_jobs(params)
72
+ when "textDocument/definition"
73
+ queue << GotoJob.definition(id: request[:id], params: request[:params])
74
+ when "textDocument/implementation"
75
+ queue << GotoJob.implementation(id: request[:id], params: request[:params])
47
76
  end
48
77
  end
49
78
 
@@ -179,6 +208,11 @@ module Steep
179
208
  id: job.id,
180
209
  result: stats_result().map(&:as_json)
181
210
  )
211
+ when GotoJob
212
+ writer.write(
213
+ id: job.id,
214
+ result: goto(job)
215
+ )
182
216
  end
183
217
  end
184
218
 
@@ -231,6 +265,40 @@ module Steep
231
265
  end
232
266
  end
233
267
  end
268
+
269
+ def goto(job)
270
+ path = Pathname(URI.parse(job.params[:textDocument][:uri]).path)
271
+ line = job.params[:position][:line] + 1
272
+ column = job.params[:position][:character]
273
+
274
+ goto_service = Services::GotoService.new(type_check: service)
275
+ locations =
276
+ case
277
+ when job.definition?
278
+ goto_service.definition(path: path, line: line, column: column)
279
+ when job.implementation?
280
+ goto_service.implementation(path: path, line: line, column: column)
281
+ else
282
+ raise
283
+ end
284
+
285
+ locations.map do |loc|
286
+ path =
287
+ case loc
288
+ when RBS::Location
289
+ Pathname(loc.buffer.name)
290
+ else
291
+ Pathname(loc.source_buffer.name)
292
+ end
293
+
294
+ path = project.absolute_path(path)
295
+
296
+ {
297
+ uri: URI.parse(path.to_s).tap {|uri| uri.scheme = "file" }.to_s,
298
+ range: loc.as_lsp_range
299
+ }
300
+ end
301
+ end
234
302
  end
235
303
  end
236
304
  end
@@ -0,0 +1,321 @@
1
+ module Steep
2
+ module Services
3
+ class GotoService
4
+ include ModuleHelper
5
+
6
+ module SourceHelper
7
+ def from_ruby?
8
+ from == :ruby
9
+ end
10
+
11
+ def from_rbs?
12
+ from == :rbs
13
+ end
14
+ end
15
+
16
+ ConstantQuery = Struct.new(:name, :from, keyword_init: true) do
17
+ include SourceHelper
18
+ end
19
+ MethodQuery = Struct.new(:name, :from, keyword_init: true) do
20
+ include SourceHelper
21
+ end
22
+ TypeNameQuery = Struct.new(:name, keyword_init: true)
23
+
24
+ attr_reader :type_check
25
+
26
+ def initialize(type_check:)
27
+ @type_check = type_check
28
+ end
29
+
30
+ def project
31
+ type_check.project
32
+ end
33
+
34
+ def implementation(path:, line:, column:)
35
+ locations = []
36
+
37
+ relative_path = project.relative_path(path)
38
+
39
+ queries = query_at(path: path, line: line, column: column)
40
+ queries.uniq!
41
+
42
+ queries.each do |query|
43
+ case query
44
+ when ConstantQuery
45
+ constant_definition_in_ruby(query.name, locations: locations)
46
+ when MethodQuery
47
+ method_locations(query.name, locations: locations, in_ruby: true, in_rbs: false)
48
+ when TypeNameQuery
49
+ type_name_locations(query.name, locations: locations)
50
+ end
51
+ end
52
+
53
+ locations.uniq
54
+ end
55
+
56
+ def definition(path:, line:, column:)
57
+ locations = []
58
+
59
+ relative_path = project.relative_path(path)
60
+
61
+ queries = query_at(path: path, line: line, column: column)
62
+ queries.uniq!
63
+
64
+ queries.each do |query|
65
+ case query
66
+ when ConstantQuery
67
+ constant_definition_in_rbs(query.name, locations: locations) if query.from_ruby?
68
+ constant_definition_in_ruby(query.name, locations: locations) if query.from_rbs?
69
+ when MethodQuery
70
+ method_locations(
71
+ query.name,
72
+ locations: locations,
73
+ in_ruby: query.from_rbs?,
74
+ in_rbs: query.from_ruby?
75
+ )
76
+ when TypeNameQuery
77
+ type_name_locations(query.name, locations: locations)
78
+ end
79
+ end
80
+
81
+ locations.uniq
82
+ end
83
+
84
+ def test_ast_location(loc, line:, column:)
85
+ return false if line < loc.line
86
+ return false if line == loc.line && column < loc.column
87
+ return false if loc.last_line < line
88
+ return false if line == loc.last_line && loc.last_column < column
89
+ true
90
+ end
91
+
92
+ def query_at(path:, line:, column:)
93
+ queries = []
94
+
95
+ relative_path = project.relative_path(path)
96
+
97
+ case
98
+ when target = type_check.source_file?(relative_path)
99
+ source = type_check.source_files[relative_path]
100
+ typing, signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
101
+ if typing
102
+ node, *parents = typing.source.find_nodes(line: line, column: column)
103
+ if node
104
+ case node.type
105
+ when :const, :casgn
106
+ if test_ast_location(node.location.name, line: line, column: column)
107
+ if module_context = typing.context_at(line: line, column: column).module_context
108
+ const_env = module_context.const_env
109
+ const = const_env.lookup_constant(module_name_from_node(node))
110
+ queries << ConstantQuery.new(name: const.name, from: :ruby)
111
+ end
112
+ end
113
+ when :def, :defs
114
+ if test_ast_location(node.location.name, line: line, column: column)
115
+ if method_context = typing.context_at(line: line, column: column).method_context
116
+ type_name = method_context.method.defined_in
117
+ name =
118
+ if method_context.method.defs.any? {|defn| defn.member.singleton? }
119
+ SingletonMethodName.new(type_name: type_name, method_name: method_context.name)
120
+ else
121
+ InstanceMethodName.new(type_name: type_name, method_name: method_context.name)
122
+ end
123
+ queries << MethodQuery.new(name: name, from: :ruby)
124
+ end
125
+ end
126
+ when :send
127
+ if test_ast_location(node.location.selector, line: line, column: column)
128
+ case call = typing.call_of(node: node)
129
+ when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
130
+ call.method_decls.each do |decl|
131
+ queries << MethodQuery.new(name: decl.method_name, from: :ruby)
132
+ end
133
+ when TypeInference::MethodCall::Untyped
134
+ # nop
135
+ when TypeInference::MethodCall::NoMethodError
136
+ # nop
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ when target_names = type_check.signature_file?(path)
143
+ target_names.each do |target_name|
144
+ signature_service = type_check.signature_services[target_name]
145
+ decls = signature_service.latest_env.declarations.select do |decl|
146
+ buffer_path = Pathname(decl.location.buffer.name)
147
+ buffer_path == relative_path || buffer_path == path
148
+ end
149
+
150
+ locator = RBS::Locator.new(decls: decls)
151
+ last, nodes = locator.find2(line: line, column: column)
152
+ case nodes[0]
153
+ when RBS::AST::Declarations::Class, RBS::AST::Declarations::Module
154
+ if last == :name
155
+ queries << ConstantQuery.new(name: nodes[0].name, from: :rbs)
156
+ end
157
+ when RBS::AST::Declarations::Constant
158
+ if last == :name
159
+ queries << ConstantQuery.new(name: nodes[0].name, from: :rbs)
160
+ end
161
+ when RBS::AST::Members::MethodDefinition
162
+ if last == :name
163
+ type_name = nodes[1].name
164
+ method_name = nodes[0].name
165
+ if nodes[0].instance?
166
+ queries << MethodQuery.new(
167
+ name: InstanceMethodName.new(type_name: type_name, method_name: method_name),
168
+ from: :rbs
169
+ )
170
+ end
171
+ if nodes[0].singleton?
172
+ queries << MethodQuery.new(
173
+ name: SingletonMethodName.new(type_name: type_name, method_name: method_name),
174
+ from: :rbs
175
+ )
176
+ end
177
+ end
178
+ when RBS::AST::Members::Include, RBS::AST::Members::Extend, RBS::AST::Members::Prepend
179
+ if last == :name
180
+ queries << TypeNameQuery.new(name: nodes[0].name)
181
+ end
182
+ when RBS::Types::ClassInstance, RBS::Types::ClassSingleton, RBS::Types::Interface, RBS::Types::Alias
183
+ if last == :name
184
+ queries << TypeNameQuery.new(name: nodes[0].name)
185
+ end
186
+ when RBS::AST::Declarations::Class::Super, RBS::AST::Declarations::Module::Self
187
+ if last == :name
188
+ queries << TypeNameQuery.new(name: nodes[0].name)
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ queries
195
+ end
196
+
197
+ def type_check_path(target:, path:, content:, line:, column:)
198
+ signature_service = type_check.signature_services[target.name]
199
+ subtyping = signature_service.current_subtyping or return
200
+ source = Source.parse(content, path: path, factory: subtyping.factory)
201
+ source = source.without_unrelated_defs(line: line, column: column)
202
+ [
203
+ Services::TypeCheckService.type_check(source: source, subtyping: subtyping),
204
+ signature_service
205
+ ]
206
+ rescue
207
+ nil
208
+ end
209
+
210
+ def constant_definition_in_rbs(name, locations:)
211
+ type_check.signature_services.each_value do |signature|
212
+ env = signature.latest_env
213
+
214
+ if entry = env.class_decls[name]
215
+ entry.decls.each do |d|
216
+ locations << d.decl.location[:name]
217
+ end
218
+ end
219
+
220
+ if entry = env.constant_decls[name]
221
+ locations << entry.decl.location[:name]
222
+ end
223
+ end
224
+
225
+ locations
226
+ end
227
+
228
+ def constant_definition_in_ruby(name, locations:)
229
+ type_check.source_files.each do |path, source|
230
+ if typing = source.typing
231
+ entry = typing.source_index.entry(constant: name)
232
+ entry.definitions.each do |node|
233
+ case node.type
234
+ when :class, :module
235
+ locations << node.children[0].location.expression
236
+ when :casgn
237
+ parent = node.children[0]
238
+ location =
239
+ if parent
240
+ parent.location.expression.join(node.location.name)
241
+ else
242
+ node.location.name
243
+ end
244
+ locations << location
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ locations
251
+ end
252
+
253
+ def method_locations(name, in_ruby:, in_rbs:, locations:)
254
+ if in_ruby
255
+ type_check.source_files.each do |path, source|
256
+ if typing = source.typing
257
+ entry = typing.source_index.entry(method: name)
258
+
259
+ if entry.definitions.empty?
260
+ if name.is_a?(SingletonMethodName) && name.method_name == :new
261
+ initialize = InstanceMethodName.new(method_name: :initialize, type_name: name.type_name)
262
+ entry = typing.source_index.entry(method: initialize)
263
+ end
264
+ end
265
+
266
+ entry.definitions.each do |node|
267
+ case node.type
268
+ when :def
269
+ locations << node.location.name
270
+ when :defs
271
+ locations << node.location.name
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ if in_rbs
279
+ type_check.signature_services.each_value do |signature|
280
+ index = signature.latest_rbs_index
281
+
282
+ entry = index.entry(method_name: name)
283
+
284
+ if entry.declarations.empty?
285
+ if name.is_a?(SingletonMethodName) && name.method_name == :new
286
+ initialize = InstanceMethodName.new(method_name: :initialize, type_name: name.type_name)
287
+ entry = index.entry(method_name: initialize)
288
+ end
289
+ end
290
+
291
+ entry.declarations.each do |decl|
292
+ case decl
293
+ when RBS::AST::Members::MethodDefinition
294
+ locations << decl.location[:name]
295
+ when RBS::AST::Members::Alias
296
+ locations << decl.location[:new_name]
297
+ when RBS::AST::Members::AttrAccessor, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrWriter
298
+ locations << decl.location[:name]
299
+ end
300
+ end
301
+ end
302
+ end
303
+
304
+ locations
305
+ end
306
+
307
+ def type_name_locations(name, locations: [])
308
+ type_check.signature_services.each_value do |signature|
309
+ index = signature.latest_rbs_index
310
+
311
+ entry = index.entry(type_name: name)
312
+ entry.declarations.each do |decl|
313
+ locations << decl.location[:name]
314
+ end
315
+ end
316
+
317
+ locations
318
+ end
319
+ end
320
+ end
321
+ end
@@ -383,6 +383,31 @@ module Steep
383
383
 
384
384
  typing
385
385
  end
386
+
387
+ def source_file?(path)
388
+ if source_files.key?(path)
389
+ project.target_for_source_path(path)
390
+ end
391
+ end
392
+
393
+ def signature_file?(path)
394
+ relative_path = project.relative_path(path)
395
+ targets = signature_services.select {|_, sig| sig.files.key?(relative_path) || sig.env_rbs_paths.include?(path) }
396
+ unless targets.empty?
397
+ targets.keys
398
+ end
399
+ end
400
+
401
+ def app_signature_file?(path)
402
+ target_names = signature_services.select {|_, sig| sig.files.key?(path) }.keys
403
+ unless target_names.empty?
404
+ target_names
405
+ end
406
+ end
407
+
408
+ def lib_signature_file?(path)
409
+ signature_services.each_value.any? {|sig| sig.env_rbs_paths.include?(path) }
410
+ end
386
411
  end
387
412
  end
388
413
  end
@@ -944,14 +944,23 @@ module Steep
944
944
  yield_self do
945
945
  name, args_node, body_node = node.children
946
946
 
947
- new = for_new_method(name,
948
- node,
949
- args: args_node.children,
950
- self_type: module_context&.instance_type,
951
- definition: module_context&.instance_definition)
947
+ new = for_new_method(
948
+ name,
949
+ node,
950
+ args: args_node.children,
951
+ self_type: module_context&.instance_type,
952
+ definition: module_context&.instance_definition
953
+ )
952
954
  new.typing.add_context_for_node(node, context: new.context)
953
955
  new.typing.add_context_for_body(node, context: new.context)
954
956
 
957
+ new.method_context.tap do |method_context|
958
+ if method_context.method
959
+ method_name = InstanceMethodName.new(type_name: method_context.method.implemented_in, method_name: name)
960
+ new.typing.source_index.add_definition(method: method_name, definition: node)
961
+ end
962
+ end
963
+
955
964
  new = new.synthesize_children(args_node)
956
965
 
957
966
  body_pair = if body_node
@@ -1006,24 +1015,43 @@ module Steep
1006
1015
  when :defs
1007
1016
  synthesize(node.children[0]).type.tap do |self_type|
1008
1017
  self_type = expand_self(self_type)
1009
- definition = case self_type
1010
- when AST::Types::Name::Instance
1011
- name = self_type.name
1012
- checker.factory.definition_builder.build_singleton(name)
1013
- when AST::Types::Name::Singleton
1014
- name = self_type.name
1015
- checker.factory.definition_builder.build_singleton(name)
1016
- end
1018
+ definition =
1019
+ case self_type
1020
+ when AST::Types::Name::Instance
1021
+ name = self_type.name
1022
+ checker.factory.definition_builder.build_instance(name)
1023
+ when AST::Types::Name::Singleton
1024
+ name = self_type.name
1025
+ checker.factory.definition_builder.build_singleton(name)
1026
+ end
1017
1027
 
1018
1028
  args_node = node.children[2]
1019
- new = for_new_method(node.children[1],
1020
- node,
1021
- args: args_node.children,
1022
- self_type: self_type,
1023
- definition: definition)
1029
+ new = for_new_method(
1030
+ node.children[1],
1031
+ node,
1032
+ args: args_node.children,
1033
+ self_type: self_type,
1034
+ definition: definition
1035
+ )
1024
1036
  new.typing.add_context_for_node(node, context: new.context)
1025
1037
  new.typing.add_context_for_body(node, context: new.context)
1026
1038
 
1039
+ new.method_context.tap do |method_context|
1040
+ if method_context.method
1041
+ name_ = node.children[1]
1042
+
1043
+ method_name =
1044
+ case self_type
1045
+ when AST::Types::Name::Instance
1046
+ InstanceMethodName.new(type_name: method_context.method.implemented_in, method_name: name_)
1047
+ when AST::Types::Name::Singleton
1048
+ SingletonMethodName.new(type_name: method_context.method.implemented_in, method_name: name_)
1049
+ end
1050
+
1051
+ new.typing.source_index.add_definition(method: method_name, definition: node)
1052
+ end
1053
+ end
1054
+
1027
1055
  new = new.synthesize_children(args_node)
1028
1056
 
1029
1057
  each_child_node(node.children[2]) do |arg|
@@ -1443,9 +1471,22 @@ module Steep
1443
1471
  constr = self
1444
1472
 
1445
1473
  name, _ = node.children
1446
- _, constr = constr.synthesize(name)
1474
+ if name.type == :const
1475
+ # skip the last constant reference
1476
+ if const_parent = name.children[0]
1477
+ _, constr = constr.synthesize(const_parent)
1478
+ end
1479
+ else
1480
+ _, constr = constr.synthesize(name)
1481
+ end
1447
1482
 
1448
1483
  for_module(node).yield_self do |constructor|
1484
+ if module_type = constructor.module_context&.module_type
1485
+ _, constructor = constructor.add_typing(name, type: module_type)
1486
+ else
1487
+ _, constructor = constructor.fallback_to_any(name)
1488
+ end
1489
+
1449
1490
  constructor.typing.source_index.add_definition(
1450
1491
  constant: constructor.module_context.class_name,
1451
1492
  definition: node
@@ -3262,8 +3303,8 @@ module Steep
3262
3303
  end
3263
3304
  else
3264
3305
  # block is not given
3265
- if (!method_type.block || method_type.block.optional?)
3266
- # Method call without block is allowed
3306
+ if !method_type.block
3307
+ # Method doesn't accept blocks
3267
3308
  unless args.block_pass_arg
3268
3309
  # OK, without block
3269
3310
  s = constraints.solution(
@@ -3297,12 +3338,16 @@ module Steep
3297
3338
  end
3298
3339
  end
3299
3340
  else
3300
- unless args.block_pass_arg
3301
- # Required block is missing
3302
- errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3303
- node: node,
3304
- method_type: method_type
3305
- )
3341
+ # Method accepts block
3342
+ if !args.block_pass_arg
3343
+ # Block pass is not given
3344
+ if method_type.block.required?
3345
+ # Required block is missing
3346
+ errors << Diagnostic::Ruby::RequiredBlockMissing.new(
3347
+ node: node,
3348
+ method_type: method_type
3349
+ )
3350
+ end
3306
3351
 
3307
3352
  s = constraints.solution(
3308
3353
  checker,
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.43.1"
2
+ VERSION = "0.44.0"
3
3
  end
@@ -26,16 +26,6 @@
26
26
  severity: ERROR
27
27
  message: Cannot detect the type of the expression
28
28
  code: Ruby::FallbackAny
29
- - range:
30
- start:
31
- line: 8
32
- character: 7
33
- end:
34
- line: 8
35
- character: 8
36
- severity: ERROR
37
- message: Cannot detect the type of the expression
38
- code: Ruby::FallbackAny
39
29
  - range:
40
30
  start:
41
31
  line: 14
@@ -0,0 +1,5 @@
1
+ target :test do
2
+ typing_options :strict
3
+ check "*.rb"
4
+ signature "*.rbs"
5
+ end
@@ -0,0 +1,3 @@
1
+ class UnsatisfiableConstraint
2
+ def foo: [A, B] (A) { (A) -> void } -> B
3
+ end
@@ -0,0 +1,27 @@
1
+ ---
2
+ - file: unsatisfiable_constraint.rb
3
+ diagnostics:
4
+ - range:
5
+ start:
6
+ line: 3
7
+ character: 0
8
+ end:
9
+ line: 6
10
+ character: 3
11
+ severity: ERROR
12
+ message: |-
13
+ Unsatisfiable constraint `::Array[untyped] <: A(2) <: ::String` is generated through (A(2)) { (A(2)) -> void } -> B(3)
14
+ ::Array[untyped] <: ::String
15
+ ::Object <: ::String
16
+ ::BasicObject <: ::String
17
+ code: Ruby::UnsatisfiableConstraint
18
+ - range:
19
+ start:
20
+ line: 5
21
+ character: 4
22
+ end:
23
+ line: 5
24
+ character: 7
25
+ severity: ERROR
26
+ message: Type `::String` does not have method `foo`
27
+ code: Ruby::NoMethod
@@ -1,4 +1,3 @@
1
-
2
1
  test = UnsatisfiableConstraint.new
3
2
 
4
3
  test.foo([]) do |x|
@@ -13,10 +13,6 @@ end
13
13
  class UnknownConstantAssigned
14
14
  end
15
15
 
16
- class UnsatisfiableConstraint
17
- def foo: [A, B] (A) { (A) -> void } -> B
18
- end
19
-
20
16
  class UnexpectedKeyword
21
17
  def foo: (foo: Integer) -> void
22
18
  end
@@ -436,32 +436,6 @@
436
436
  | (::Rational) -> ::Rational
437
437
  | (::Complex) -> ::Complex
438
438
  code: Ruby::UnresolvedOverloading
439
- - file: unsatisfiable_constraint.rb
440
- diagnostics:
441
- - range:
442
- start:
443
- line: 4
444
- character: 0
445
- end:
446
- line: 7
447
- character: 3
448
- severity: ERROR
449
- message: |-
450
- Unsatisfiable constraint `::Array[untyped] <: A(1) <: ::String` is generated through (A(1)) { (A(1)) -> void } -> B(2)
451
- ::Array[untyped] <: ::String
452
- ::Object <: ::String
453
- ::BasicObject <: ::String
454
- code: Ruby::UnsatisfiableConstraint
455
- - range:
456
- start:
457
- line: 6
458
- character: 4
459
- end:
460
- line: 6
461
- character: 7
462
- severity: ERROR
463
- message: Type `::String` does not have method `foo`
464
- code: Ruby::NoMethod
465
439
  - file: unsupported_syntax.rb
466
440
  diagnostics:
467
441
  - range:
@@ -0,0 +1,8 @@
1
+ class Issue372
2
+ def f(&block)
3
+ end
4
+
5
+ def g(&block)
6
+ f(&block)
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ class Issue372
2
+ def f: () ?{ () -> void } -> void
3
+ def g: () ?{ () -> void } -> void
4
+ end
@@ -58,15 +58,3 @@
58
58
  severity: ERROR
59
59
  message: Type `::String` does not have method `ggggg`
60
60
  code: Ruby::NoMethod
61
- - file: thread.rb
62
- diagnostics:
63
- - range:
64
- start:
65
- line: 6
66
- character: 13
67
- end:
68
- line: 7
69
- character: 3
70
- severity: ERROR
71
- message: The method cannot be called with a block
72
- code: Ruby::UnexpectedBlockGiven
data/steep.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
34
34
  spec.add_runtime_dependency "listen", "~> 3.0"
35
35
  spec.add_runtime_dependency "language_server-protocol", ">= 3.15", "< 4.0"
36
- spec.add_runtime_dependency "rbs", "~> 1.1.0"
36
+ spec.add_runtime_dependency "rbs", ">= 1.2.0"
37
37
  spec.add_runtime_dependency "parallel", ">= 1.0.0"
38
38
  spec.add_runtime_dependency "terminal-table", ">= 2", "< 4"
39
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: steep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.43.1
4
+ version: 0.44.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Soutaro Matsumoto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-01 00:00:00.000000000 Z
11
+ date: 2021-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -96,16 +96,16 @@ dependencies:
96
96
  name: rbs
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - "~>"
99
+ - - ">="
100
100
  - !ruby/object:Gem::Version
101
- version: 1.1.0
101
+ version: 1.2.0
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
- - - "~>"
106
+ - - ">="
107
107
  - !ruby/object:Gem::Version
108
- version: 1.1.0
108
+ version: 1.2.0
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: parallel
111
111
  requirement: !ruby/object:Gem::Requirement
@@ -236,6 +236,7 @@ files:
236
236
  - lib/steep/services/completion_provider.rb
237
237
  - lib/steep/services/content_change.rb
238
238
  - lib/steep/services/file_loader.rb
239
+ - lib/steep/services/goto_service.rb
239
240
  - lib/steep/services/hover_content.rb
240
241
  - lib/steep/services/path_assignment.rb
241
242
  - lib/steep/services/signature_service.rb
@@ -326,6 +327,10 @@ files:
326
327
  - smoke/diagnostics-rbs/unknown-method-alias.rbs
327
328
  - smoke/diagnostics-rbs/unknown-type-name-2.rbs
328
329
  - smoke/diagnostics-rbs/unknown-type-name.rbs
330
+ - smoke/diagnostics-ruby-unsat/Steepfile
331
+ - smoke/diagnostics-ruby-unsat/a.rbs
332
+ - smoke/diagnostics-ruby-unsat/test_expectations.yml
333
+ - smoke/diagnostics-ruby-unsat/unsatisfiable_constraint.rb
329
334
  - smoke/diagnostics/Steepfile
330
335
  - smoke/diagnostics/a.rbs
331
336
  - smoke/diagnostics/argument_type_mismatch.rb
@@ -354,7 +359,6 @@ files:
354
359
  - smoke/diagnostics/unexpected_yield.rb
355
360
  - smoke/diagnostics/unknown_constant_assigned.rb
356
361
  - smoke/diagnostics/unresolved_overloading.rb
357
- - smoke/diagnostics/unsatisfiable_constraint.rb
358
362
  - smoke/diagnostics/unsupported_syntax.rb
359
363
  - smoke/dstr/Steepfile
360
364
  - smoke/dstr/a.rb
@@ -455,6 +459,8 @@ files:
455
459
  - smoke/regression/issue_328.rbs
456
460
  - smoke/regression/issue_332.rb
457
461
  - smoke/regression/issue_332.rbs
462
+ - smoke/regression/issue_372.rb
463
+ - smoke/regression/issue_372.rbs
458
464
  - smoke/regression/masgn.rb
459
465
  - smoke/regression/poly_new.rb
460
466
  - smoke/regression/poly_new.rbs