steep 0.49.1 → 0.50.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: 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