steep 0.49.1 → 0.50.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: 44df04a21f7dc9d7514ad6acb34549f40fe0b4d962840c29ced4ceca926fd000
4
- data.tar.gz: 15ac298575e8af8d29c1b1ab5e2894f3e62677dedd58b166d147d3d1649026d3
3
+ metadata.gz: 6ff552f6d8f112693dc9969f20d851ddbc6d7d2f16cebd45f802a5d703618b58
4
+ data.tar.gz: 723464fe34c81c3e83b66338dfc6fd3e7c3da2aaf30c1fc48116c7de794ca961
5
5
  SHA512:
6
- metadata.gz: cdc1e1dea1ef2aa9cebd6d40a850894340e2d0ed26f8a982c81c80d384522712c442a499ef12419b0b224cd1bb659944198b8dc897eea692e4d6c06284c86381
7
- data.tar.gz: 22a8d55bd324e3527ffd2a8886bbf7fe28e766ab2dab1e175c43a08b37d256c647f39a05e7890ed5b2726934428bb303c1fe98c45ad07166df2fb2698cc835af
6
+ metadata.gz: bf9f83791cfb636c65a26f404fb164fa7fc3906376e36a79a1ca4ae7eeab8020e23c5b9ce5e649a38ed79080d0aa2c00fe53a5cce8a1c76c259237f38880bbf2
7
+ data.tar.gz: 25bd9e11db283acdfc957682860e674d544b82be1b5a68bc87ca96888907966b721301c937cdaa3e06bf0226ec82fa8c78eacfaf4987637a601f75ca8d8e950c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## master
4
4
 
5
+ # 0.50.0 (2022-03-22)
6
+
7
+ * CLI option for override steep command at spawn worker ([\#511](https://github.com/soutaro/steep/pull/511))
8
+ * LSP related improvements for Sublime LSP ([\#513](https://github.com/soutaro/steep/pull/513))
9
+ * Support Windows environment ([\#514](https://github.com/soutaro/steep/pull/514))
10
+ * Let `&:foo` proc work with methods with optional parameters ([\#516](https://github.com/soutaro/steep/pull/516))
11
+ * Fix unexpected error when or-asgn/and-asgn ([\#517](https://github.com/soutaro/steep/pull/517))
12
+ * Fix goto-definition from method call inside block ([\#518](https://github.com/soutaro/steep/pull/518))
13
+ * Better splat in array typing ([\#519](https://github.com/soutaro/steep/pull/519))
14
+
5
15
  ## 0.49.1 (2022-03-11)
6
16
 
7
17
  * Fix lambda typing ([\#506](https://github.com/soutaro/steep/pull/506))
data/Gemfile CHANGED
@@ -9,4 +9,6 @@ gem "without_steep_types", path: "test/gems/without_steep_types"
9
9
  gem "rake"
10
10
  gem "minitest", "~> 5.15"
11
11
  gem "minitest-hooks"
12
- gem "stackprof"
12
+ group :stackprof, optional: true do
13
+ gem "stackprof"
14
+ end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.49.1)
4
+ steep (0.50.0)
5
5
  activesupport (>= 5.1)
6
6
  language_server-protocol (>= 3.15, < 4.0)
7
7
  listen (~> 3.0)
@@ -41,7 +41,7 @@ GEM
41
41
  minitest (5.15.0)
42
42
  minitest-hooks (1.5.0)
43
43
  minitest (> 5.3)
44
- parallel (1.21.0)
44
+ parallel (1.22.0)
45
45
  parser (3.1.1.0)
46
46
  ast (~> 2.4.1)
47
47
  rainbow (3.1.1)
data/lib/steep/cli.rb CHANGED
@@ -72,6 +72,11 @@ module Steep
72
72
  opts.on("-j N", "--jobs=N", "Specify the number of type check workers (defaults: #{default})") do |count|
73
73
  command.jobs_count = Integer(count) if Integer(count) > 0
74
74
  end
75
+
76
+ command.steep_command = "steep"
77
+ opts.on("--steep-command=COMMAND", "Specify command to exec Steep CLI for worker (defaults: steep)") do |cmd|
78
+ command.steep_command = cmd
79
+ end
75
80
  end
76
81
 
77
82
  def process_init
@@ -39,6 +39,7 @@ module Steep
39
39
  steepfile: project.steepfile_path,
40
40
  args: command_line_patterns,
41
41
  delay_shutdown: true,
42
+ steep_command: steep_command,
42
43
  count: jobs_count
43
44
  )
44
45
 
@@ -37,8 +37,8 @@ module Steep
37
37
  def run
38
38
  @project = load_config()
39
39
 
40
- interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path)
41
- typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: [], count: jobs_count)
40
+ interaction_worker = Server::WorkerProcess.spawn_worker(:interaction, name: "interaction", steepfile: project.steepfile_path, steep_command: steep_command)
41
+ typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: [], steep_command: steep_command, count: jobs_count)
42
42
 
43
43
  master = Server::Master.new(
44
44
  project: project,
@@ -129,6 +129,7 @@ module Steep
129
129
  steepfile: project.steepfile_path,
130
130
  delay_shutdown: true,
131
131
  args: command_line_patterns,
132
+ steep_command: steep_command,
132
133
  count: jobs_count
133
134
  )
134
135
 
@@ -2,7 +2,7 @@ module Steep
2
2
  module Drivers
3
3
  module Utils
4
4
  module JobsCount
5
- attr_accessor :jobs_count
5
+ attr_accessor :jobs_count, :steep_command
6
6
  end
7
7
  end
8
8
  end
@@ -41,7 +41,7 @@ module Steep
41
41
  server_reader = LanguageServer::Protocol::Transport::Io::Reader.new(server_read)
42
42
  server_writer = LanguageServer::Protocol::Transport::Io::Writer.new(server_write)
43
43
 
44
- typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: dirs.map(&:to_s), count: jobs_count)
44
+ typecheck_workers = Server::WorkerProcess.spawn_typecheck_workers(steepfile: project.steepfile_path, args: dirs.map(&:to_s), steep_command: steep_command, count: jobs_count)
45
45
 
46
46
  master = Server::Master.new(
47
47
  project: project,
@@ -879,6 +879,11 @@ module Steep
879
879
  !has_positional? && !has_keywords?
880
880
  end
881
881
 
882
+ # Returns true if all arguments are non-required.
883
+ def optional?
884
+ required.empty? && required_keywords.empty?
885
+ end
886
+
882
887
  # self + params returns a new params for overloading.
883
888
  #
884
889
  def +(other)
data/lib/steep/project.rb CHANGED
@@ -16,7 +16,21 @@ module Steep
16
16
  steepfile_path.parent
17
17
  end
18
18
 
19
- def relative_path(path)
19
+ def relative_path(orig_path)
20
+ path = if Gem.win_platform?
21
+ path_str = URI.decode_www_form_component(
22
+ orig_path.to_s.delete_prefix("/")
23
+ )
24
+ unless path_str.start_with?(%r{[a-z]:/}i)
25
+ # FIXME: Sometimes drive letter is missing, taking from base_dir
26
+ path_str = base_dir.to_s.split("/")[0] + "/" + path_str
27
+ end
28
+ Pathname.new(
29
+ path_str
30
+ )
31
+ else
32
+ orig_path
33
+ end
20
34
  path.relative_path_from(base_dir)
21
35
  end
22
36
 
@@ -89,7 +89,7 @@ module Steep
89
89
  end
90
90
 
91
91
  LSP::Interface::Hover.new(
92
- contents: { kind: "markdown", value: format_hover(content) },
92
+ contents: { kind: "markdown", value: format_hover(content)&.gsub(/<!--(?~-->)-->/, "") },
93
93
  range: range
94
94
  )
95
95
  end
@@ -271,7 +271,7 @@ HOVER
271
271
  ),
272
272
  end: LanguageServer::Protocol::Interface::Position.new(
273
273
  line: job.line - 1,
274
- character: job.column - prefix.size
274
+ character: job.column
275
275
  )
276
276
  )
277
277
 
@@ -49,9 +49,14 @@ module Steep
49
49
  end
50
50
 
51
51
  def checking_path?(path)
52
- library_paths.include?(path) ||
53
- signature_paths.include?(path) ||
54
- code_paths.include?(path)
52
+ [library_paths, signature_paths, code_paths].any? do |paths|
53
+ if Gem.win_platform?
54
+ # FIXME: Sometimes drive letter is missing, so comparing without drive letter.
55
+ paths.any? {|p| p.to_s.split("/", 2)[1] == path.to_s.split("/", 2)[1]}
56
+ else
57
+ paths.include?(path)
58
+ end
59
+ end
55
60
  end
56
61
 
57
62
  def checked(path)
@@ -739,7 +744,7 @@ module Steep
739
744
  { kind: "end" }
740
745
  else
741
746
  progress_string = ("▮"*(percentage/5)) + ("▯"*(20 - percentage/5))
742
- { kind: "report", percentage: percentage, message: "#{progress_string} (#{percentage}%)" }
747
+ { kind: "report", percentage: percentage, message: "#{progress_string}" }
743
748
  end
744
749
 
745
750
  job_queue << SendMessageJob.to_client(
@@ -135,6 +135,21 @@ module Steep
135
135
  end
136
136
 
137
137
  def handle_job(job)
138
+ job_path = if job.respond_to?(:path)
139
+ if Gem.win_platform?
140
+ # FIXME: Sometimes drive letter is missing, using base_dir
141
+ if job.path.to_s.start_with?(%r{/[a-z](:|%3A)/}i)
142
+ job.path.to_s
143
+ else
144
+ "/#{project.base_dir.to_s.split("/").first}/#{job.path}"
145
+ end
146
+ else
147
+ job.path.to_s
148
+ end
149
+ else
150
+ nil
151
+ end
152
+
138
153
  case job
139
154
  when StartTypeCheckJob
140
155
  Steep.logger.info { "Processing StartTypeCheckJob for guid=#{job.guid}" }
@@ -149,7 +164,7 @@ module Steep
149
164
  writer.write(
150
165
  method: :"textDocument/publishDiagnostics",
151
166
  params: LSP::Interface::PublishDiagnosticsParams.new(
152
- uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
167
+ uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
153
168
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
154
169
  )
155
170
  )
@@ -167,7 +182,7 @@ module Steep
167
182
  writer.write(
168
183
  method: :"textDocument/publishDiagnostics",
169
184
  params: LSP::Interface::PublishDiagnosticsParams.new(
170
- uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
185
+ uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
171
186
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
172
187
  )
173
188
  )
@@ -186,7 +201,7 @@ module Steep
186
201
  writer.write(
187
202
  method: :"textDocument/publishDiagnostics",
188
203
  params: LSP::Interface::PublishDiagnosticsParams.new(
189
- uri: URI.parse(job.path.to_s).tap {|uri| uri.scheme = "file"},
204
+ uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
190
205
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
191
206
  )
192
207
  )
@@ -268,7 +283,7 @@ module Steep
268
283
  line = job.params[:position][:line] + 1
269
284
  column = job.params[:position][:character]
270
285
 
271
- goto_service = Services::GotoService.new(type_check: service)
286
+ goto_service = Services::GotoService.new(type_check: service, assignment: assignment)
272
287
  locations =
273
288
  case
274
289
  when job.definition?
@@ -18,13 +18,17 @@ module Steep
18
18
  @index = index
19
19
  end
20
20
 
21
- def self.spawn_worker(type, name:, steepfile:, options: [], delay_shutdown: false, index: nil)
22
- log_level = %w(debug info warn error fatal unknown)[Steep.logger.level]
21
+ def self.spawn_worker(type, name:, steepfile:, steep_command: "steep", options: [], delay_shutdown: false, index: nil)
22
+ args = ["--name=#{name}", "--steepfile=#{steepfile}"]
23
+ args << (%w(debug info warn error fatal unknown)[Steep.logger.level].yield_self {|log_level| "--log-level=#{log_level}" })
24
+ if Steep.log_output.is_a?(String)
25
+ args << "--log-output=#{Steep.log_output}"
26
+ end
23
27
  command = case type
24
28
  when :interaction
25
- ["steep", "worker", "--interaction", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}", *options]
29
+ [steep_command, "worker", "--interaction", *args, *options]
26
30
  when :typecheck
27
- ["steep", "worker", "--typecheck", "--name=#{name}", "--log-level=#{log_level}", "--steepfile=#{steepfile}", *options]
31
+ [steep_command, "worker", "--typecheck", *args, *options]
28
32
  else
29
33
  raise "Unknown type: #{type}"
30
34
  end
@@ -33,7 +37,11 @@ module Steep
33
37
  command << "--delay-shutdown"
34
38
  end
35
39
 
36
- stdin, stdout, thread = Open3.popen2(*command, pgroup: true)
40
+ stdin, stdout, thread = if Gem.win_platform?
41
+ Open3.popen2(*command, new_pgroup: true)
42
+ else
43
+ Open3.popen2(*command, pgroup: true)
44
+ end
37
45
  stderr = nil
38
46
 
39
47
  writer = LanguageServer::Protocol::Transport::Io::Writer.new(stdin)
@@ -42,11 +50,12 @@ module Steep
42
50
  new(reader: reader, writer: writer, stderr: stderr, wait_thread: thread, name: name, index: index)
43
51
  end
44
52
 
45
- def self.spawn_typecheck_workers(steepfile:, args:, count: [Etc.nprocessors - 1, 1].max, delay_shutdown: false)
53
+ def self.spawn_typecheck_workers(steepfile:, args:, steep_command: "steep", count: [Etc.nprocessors - 1, 1].max, delay_shutdown: false)
46
54
  count.times.map do |i|
47
55
  spawn_worker(:typecheck,
48
56
  name: "typecheck@#{i}",
49
57
  steepfile: steepfile,
58
+ steep_command: steep_command,
50
59
  options: ["--max-index=#{count}", "--index=#{i}", *args],
51
60
  delay_shutdown: delay_shutdown,
52
61
  index: i)
@@ -21,10 +21,11 @@ module Steep
21
21
  end
22
22
  TypeNameQuery = Struct.new(:name, keyword_init: true)
23
23
 
24
- attr_reader :type_check
24
+ attr_reader :type_check, :assignment
25
25
 
26
- def initialize(type_check:)
26
+ def initialize(type_check:, assignment:)
27
27
  @type_check = type_check
28
+ @assignment = assignment
28
29
  end
29
30
 
30
31
  def project
@@ -78,7 +79,17 @@ module Steep
78
79
  end
79
80
  end
80
81
 
81
- locations.uniq
82
+ # Drop un-assigned paths here.
83
+ # The path assignment makes sense only for `.rbs` files, because un-assigned `.rb` files are already skipped since they are not type checked.
84
+ #
85
+ locations.uniq.select do |loc|
86
+ case loc
87
+ when RBS::Location
88
+ assignment =~ loc.name
89
+ else
90
+ true
91
+ end
92
+ end
82
93
  end
83
94
 
84
95
  def test_ast_location(loc, line:, column:)
@@ -100,6 +111,7 @@ module Steep
100
111
  typing, signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
101
112
  if typing
102
113
  node, *parents = typing.source.find_nodes(line: line, column: column)
114
+
103
115
  if node
104
116
  case node.type
105
117
  when :const, :casgn
@@ -125,7 +137,10 @@ module Steep
125
137
  end
126
138
  when :send
127
139
  if test_ast_location(node.location.selector, line: line, column: column)
128
- node = parents[0] if parents[0]&.type == :block
140
+ if (parent = parents[0]) && parent.type == :block && parent.children[0] == node
141
+ node = parents[0]
142
+ end
143
+
129
144
  case call = typing.call_of(node: node)
130
145
  when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
131
146
  call.method_decls.each do |decl|
@@ -2275,13 +2275,10 @@ module Steep
2275
2275
  type, constr = synthesize(rhs, hint: hint)
2276
2276
  constr.ivasgn(asgn, type)
2277
2277
  when :send
2278
- rhs_ = node.updated(:send,
2279
- [
2280
- asgn.children[0],
2281
- :"#{asgn.children[1]}=",
2282
- asgn.children[2],
2283
- rhs
2284
- ])
2278
+ children = asgn.children.dup
2279
+ children[1] = :"#{children[1]}="
2280
+ send_arg_nodes = [*children, rhs]
2281
+ rhs_ = node.updated(:send, send_arg_nodes)
2285
2282
  node_type = case node.type
2286
2283
  when :or_asgn
2287
2284
  :or
@@ -2348,11 +2345,11 @@ module Steep
2348
2345
  interface = calculate_interface(param_type, private: true)
2349
2346
  method = interface.methods[value.children[0]]
2350
2347
  if method
2351
- return_types = method.method_types.select {|method_type|
2352
- method_type.type.params.empty?
2353
- }.map {|method_type|
2354
- method_type.type.return_type
2355
- }
2348
+ return_types = method.method_types.filter_map do |method_type|
2349
+ if method_type.type.params.optional?
2350
+ method_type.type.return_type
2351
+ end
2352
+ end
2356
2353
 
2357
2354
  unless return_types.empty?
2358
2355
  type = AST::Types::Proc.new(
@@ -2360,7 +2357,7 @@ module Steep
2360
2357
  params: Interface::Function::Params.empty.with_first_param(
2361
2358
  Interface::Function::Params::PositionalParams::Required.new(param_type)
2362
2359
  ),
2363
- return_type: AST::Types::Union.build(types: return_types),
2360
+ return_type: return_types[0],
2364
2361
  location: nil
2365
2362
  ),
2366
2363
  block: nil
@@ -3948,6 +3945,20 @@ module Steep
3948
3945
  constr.add_typing(node, type: AST::Types::Tuple.new(types: element_types))
3949
3946
  end
3950
3947
 
3948
+ def try_convert(type, method)
3949
+ interface = checker.factory.interface(type, private: false)
3950
+ if entry = interface.methods[method]
3951
+ method_type = entry.method_types.find do |method_type|
3952
+ method_type.type.params.optional?
3953
+ end
3954
+
3955
+ method_type.type.return_type
3956
+ end
3957
+ rescue => exn
3958
+ Steep.log_error(exn, message: "Unexpected error when converting #{type.to_s} with #{method}")
3959
+ nil
3960
+ end
3961
+
3951
3962
  def try_array_type(node, hint)
3952
3963
  element_hint = hint ? hint.args[0] : nil
3953
3964
 
@@ -3958,8 +3969,14 @@ module Steep
3958
3969
  case child.type
3959
3970
  when :splat
3960
3971
  type, constr = constr.synthesize(child.children[0], hint: hint)
3961
- if AST::Builtin::Array.instance_type?(type)
3972
+
3973
+ type = try_convert(type, :to_a) || type
3974
+
3975
+ case
3976
+ when AST::Builtin::Array.instance_type?(type)
3962
3977
  element_types << type.args[0]
3978
+ when type.is_a?(AST::Types::Tuple)
3979
+ element_types.push(*type.types)
3963
3980
  else
3964
3981
  element_types.push(*flatten_array_elements(type))
3965
3982
  end
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "0.49.1"
2
+ VERSION = "0.50.0"
3
3
  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.49.1
4
+ version: 0.50.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: 2022-03-11 00:00:00.000000000 Z
11
+ date: 2022-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser