steep 0.43.1 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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