steep 1.0.0 → 1.0.1

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: 899bca0fd211114d52e015acc844ee4648eadb6b739af911f7ac73560cedef83
4
- data.tar.gz: 2fe5b52342a6b932d5ce08f5b78c417e43cda38db378c35d1b31891ca7c79465
3
+ metadata.gz: 1d153db54fad9f22a3b38fe9a1a2539c11d50b6efc34cea1cba69b2849c69c1e
4
+ data.tar.gz: 98dfeb1b9a044c6ef30d880d5deda10fb97143e82807743c9f06c4e7aeff6663
5
5
  SHA512:
6
- metadata.gz: 20e09462ddef35a3aa00a19d6f7cb86f2862c9ab700131bf439fc7750b088d43d1f2bb3380dfdf57a0c881d58c885b789d47eba35d7e146156809511e4e1c64c
7
- data.tar.gz: dfa20a13b3b25575419ad1c9ac2417b1b873a0732eded5fa643e6e5768b644e27ea07c8d83bf9ae5e333b872d8f039aba111bee4d7ae2a48034dc06047265fee
6
+ metadata.gz: 6c7b1f95050bf14a7fbf1c521318648c3d99fd6750fff30c79d1bb84a55582dc4f74da0aec91f7ddfc7736c2a571caa99bc462865b42c1335659a6a1e44fe684
7
+ data.tar.gz: 7708ef12ea5754f432a2c71e347400ebd24cbcab9e69fab21783c1be00c000b3420c8f88db1f83bb2ed58a525a3a6f34f044ea421c2be8f53becf64dfbc7fdda
@@ -0,0 +1,34 @@
1
+ name: Run test on Windows
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request: {}
8
+
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ matrix:
13
+ ruby_version:
14
+ - "2.7"
15
+ - "3.0"
16
+ - "3.1"
17
+ task:
18
+ - test
19
+ # - test:output Ignored because the order of diagnostics changes somehow
20
+ - build
21
+ runs-on: windows-latest
22
+ steps:
23
+ - uses: actions/checkout@v3
24
+ - uses: ruby/setup-ruby@v1
25
+ with:
26
+ ruby-version: ${{ matrix.ruby_version }}
27
+ - name: Run test
28
+ run: |
29
+ git config --global --add safe.directory /__w/steep/steep
30
+ ruby -v
31
+ gem install bundler
32
+ bundle install --jobs 4 --retry 3
33
+ bin/setup
34
+ bundle exec rake ${{matrix.task}}
@@ -1,4 +1,4 @@
1
- name: Ruby
1
+ name: Run test on container
2
2
 
3
3
  on:
4
4
  push:
@@ -8,7 +8,6 @@ on:
8
8
 
9
9
  jobs:
10
10
  test:
11
- runs-on: "ubuntu-latest"
12
11
  strategy:
13
12
  matrix:
14
13
  container_tag:
@@ -20,6 +19,7 @@ jobs:
20
19
  - test
21
20
  - test:output
22
21
  - build
22
+ runs-on: ubuntu-latest
23
23
  container:
24
24
  image: rubylang/ruby:${{ matrix.container_tag }}
25
25
  steps:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.0.1 (2022-06-16)
6
+
7
+ This is the first patch release for Steep 1.0.
8
+ However, this release includes one non-trivial type system update, [\#570](https://github.com/soutaro/steep/pull/570), which adds a special typing rule for `Hash#compact` like `Array#compact`.
9
+ The change will make type checking more permissive and precise, so no new error won't be reported with the fix.
10
+
11
+ ### Type checker
12
+
13
+ * Support shorthand hash for Ruby 3.1 ([\#567](https://github.com/soutaro/steep/pull/567))
14
+ * Fix super and zsuper with block ([\#568](https://github.com/soutaro/steep/pull/568))
15
+ * Apply logic-type evaluation only if the node is `:send` ([\#569](https://github.com/soutaro/steep/pull/569))
16
+ * Add support for `Hash#compact` ([\#570](https://github.com/soutaro/steep/pull/570))
17
+ * Use given `const_env` when making a new `ModuleContext` ([\#575](https://github.com/soutaro/steep/pull/575))
18
+ * Graceful, hopefully, error handling with undefined outer module ([\#576](https://github.com/soutaro/steep/pull/576))
19
+ * Type check anonymous block forwarding ([\#577](https://github.com/soutaro/steep/pull/577))
20
+ * Incompatible default value is a type error ([\#578](https://github.com/soutaro/steep/pull/578))
21
+ * Load `ChildrenLevel` helper in `AST::Types::Proc` ([\#584](https://github.com/soutaro/steep/pull/584))
22
+ * Type check `gvar` and `gvasgn` in methods([\#579](https://github.com/soutaro/steep/pull/579))
23
+ * Avoid `UnexpectedError` when assigning untyped singleton class ([\#586](https://github.com/soutaro/steep/pull/586))
24
+
25
+ ### Tool
26
+
27
+ * Improve Windows support ([\#561](https://github.com/soutaro/steep/pull/561), [\#573](https://github.com/soutaro/steep/pull/573))
28
+ * Test if `.ruby-version` exists before `rvm do` in binstub ([\#558](https://github.com/soutaro/steep/pull/558))
29
+ * Fix typo ([\#564](https://github.com/soutaro/steep/pull/564))
30
+ * Ignore `untitled:` URIs in LSP ([\#580](https://github.com/soutaro/steep/pull/580))
31
+
32
+ ### Miscellaneous
33
+
34
+ * Fix test name ([\#565](https://github.com/soutaro/steep/pull/565), [\#566](https://github.com/soutaro/steep/pull/566), [\#585](https://github.com/soutaro/steep/pull/585))
35
+ * Remove some unused code except tests ([\#587](https://github.com/soutaro/steep/pull/587))
36
+
5
37
  ## 1.0.0 (2022-05-20)
6
38
 
7
39
  * Add special typing rule for `Array#compact` ([\#555](https://github.com/soutaro/steep/pull/555))
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gem "with_steep_types", path: "test/gems/with_steep_types"
7
7
  gem "without_steep_types", path: "test/gems/without_steep_types"
8
8
 
9
9
  gem "rake"
10
- gem "minitest", "~> 5.15"
10
+ gem "minitest", "~> 5.16"
11
11
  gem "minitest-hooks"
12
12
  group :stackprof, optional: true do
13
13
  gem "stackprof"
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (1.0.0)
4
+ steep (1.0.1)
5
5
  activesupport (>= 5.1)
6
6
  language_server-protocol (>= 3.15, < 4.0)
7
7
  listen (~> 3.0)
8
8
  parallel (>= 1.0.0)
9
- parser (>= 3.0)
9
+ parser (>= 3.1)
10
10
  rainbow (>= 2.2.2, < 4.0)
11
11
  rbs (>= 2.3.2)
12
12
  terminal-table (>= 2, < 4)
@@ -38,7 +38,7 @@ GEM
38
38
  listen (3.7.1)
39
39
  rb-fsevent (~> 0.10, >= 0.10.3)
40
40
  rb-inotify (~> 0.9, >= 0.9.10)
41
- minitest (5.15.0)
41
+ minitest (5.16.1)
42
42
  minitest-hooks (1.5.0)
43
43
  minitest (> 5.3)
44
44
  minitest-slow_test (0.2.0)
@@ -51,7 +51,7 @@ GEM
51
51
  rb-fsevent (0.11.1)
52
52
  rb-inotify (0.10.1)
53
53
  ffi (~> 1.0)
54
- rbs (2.4.0)
54
+ rbs (2.5.1)
55
55
  stackprof (0.2.19)
56
56
  terminal-table (3.0.2)
57
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -63,7 +63,7 @@ PLATFORMS
63
63
  ruby
64
64
 
65
65
  DEPENDENCIES
66
- minitest (~> 5.15)
66
+ minitest (~> 5.16)
67
67
  minitest-hooks
68
68
  minitest-slow_test
69
69
  rake
@@ -16,7 +16,7 @@ module Steep
16
16
  args << Builtin.any_type
17
17
  end
18
18
  end
19
- arity == args.size or raise "Mulformed instance type: name=#{module_name}, args=#{args}"
19
+ arity == args.size or raise "Malformed instance type: name=#{module_name}, args=#{args}"
20
20
 
21
21
  Types::Name::Instance.new(name: module_name, args: args)
22
22
  end
@@ -28,7 +28,7 @@ module Steep
28
28
  def instance_type?(type, args: nil)
29
29
  if type.is_a?(Types::Name::Instance)
30
30
  if args
31
- arity == args.size or raise "Mulformed instance type: name=#{module_name}, args=#{args}"
31
+ arity == args.size or raise "Malformed instance type: name=#{module_name}, args=#{args}"
32
32
  type.name == module_name && type.args == args
33
33
  else
34
34
  type.name == module_name && type.args.size == arity
@@ -9,6 +9,12 @@ module Steep
9
9
 
10
10
  attr_reader :type_interface_cache
11
11
 
12
+ def inspect
13
+ s = "#<%s:%#018x " % [self.class, object_id]
14
+ s << "@definition_builder=#<%s:%#018x>" % [definition_builder.class, definition_builder.object_id]
15
+ s + ">"
16
+ end
17
+
12
18
  def initialize(builder:)
13
19
  @definition_builder = builder
14
20
 
@@ -282,8 +288,6 @@ module Steep
282
288
  fvs = self_type.free_variables()
283
289
 
284
290
  type_params = []
285
- alpha_vars = []
286
- alpha_types = []
287
291
 
288
292
  conflicting_names = method_type.type_params.each.with_object([]) do |param, names|
289
293
  names << params.name if fvs.include?(param.name)
@@ -45,6 +45,8 @@ module Steep
45
45
  end
46
46
  end
47
47
 
48
+ include Helper::ChildrenLevel
49
+
48
50
  def level
49
51
  children = type.params.each_type.to_a + [type.return_type]
50
52
  if block
data/lib/steep/cli.rb CHANGED
@@ -271,7 +271,9 @@ if type "rbenv" > /dev/null 2>&1; then
271
271
  STEEP="rbenv exec ${STEEP}"
272
272
  else
273
273
  if type "rvm" > /dev/null 2>&1; then
274
- STEEP="rvm ${ROOT_DIR} do ${STEEP}"
274
+ if [ -e ${ROOT_DIR}/.ruby-version ]; then
275
+ STEEP="rvm ${ROOT_DIR} do ${STEEP}"
276
+ fi
275
277
  fi
276
278
  fi
277
279
 
@@ -296,7 +296,7 @@ module Steep
296
296
  attr_reader :method_type
297
297
 
298
298
  def initialize(node:, method_type:)
299
- super(node: node, location: node.loc.selector)
299
+ super(node: node, location: (node.type == :super || node.type == :zsuper) ? node.loc.keyword : node.loc.selector)
300
300
  @method_type = method_type
301
301
  end
302
302
 
@@ -139,7 +139,7 @@ module Steep
139
139
  missing_count = 0
140
140
 
141
141
  ns = notifications.each.with_object({}) do |notification, hash|
142
- path = project.relative_path(Pathname(URI.parse(notification[:uri]).path))
142
+ path = project.relative_path(Steep::PathHelper.to_pathname(notification[:uri]))
143
143
  hash[path] = notification[:diagnostics]
144
144
  end
145
145
 
@@ -186,7 +186,7 @@ module Steep
186
186
  end
187
187
 
188
188
  ns = notifications.each.with_object({}) do |notification, hash|
189
- path = project.relative_path(Pathname(URI.parse(notification[:uri]).path))
189
+ path = project.relative_path(Steep::PathHelper.to_pathname(notification[:uri]))
190
190
  hash[path] = notification[:diagnostics]
191
191
  end
192
192
 
@@ -215,7 +215,7 @@ module Steep
215
215
  total = errors.sum {|notification| notification[:diagnostics].size }
216
216
 
217
217
  errors.each do |notification|
218
- path = Pathname(URI.parse(notification[:uri]).path)
218
+ path = Steep::PathHelper.to_pathname(notification[:uri])
219
219
  buffer = RBS::Buffer.new(name: project.relative_path(path), content: path.read)
220
220
  printer = DiagnosticPrinter.new(buffer: buffer, stdout: stdout)
221
221
 
@@ -0,0 +1,22 @@
1
+ module Steep
2
+ module PathHelper
3
+ module_function
4
+
5
+ def to_pathname(uri, dosish: Gem.win_platform?)
6
+ uri = URI.parse(uri)
7
+ if uri.scheme == "file"
8
+ path = uri.path
9
+ path.sub!(%r{^/([a-zA-Z])(:|%3A)//?}i, '\1:/') if dosish
10
+ Pathname(path)
11
+ end
12
+ end
13
+
14
+ def to_uri(path, dosish: Gem.win_platform?)
15
+ str_path = path.to_s
16
+ if dosish
17
+ str_path.insert(0, "/") if str_path[0] != "/"
18
+ end
19
+ URI::File.build(path: str_path)
20
+ end
21
+ end
22
+ end
data/lib/steep/project.rb CHANGED
@@ -16,22 +16,10 @@ module Steep
16
16
  steepfile_path.parent
17
17
  end
18
18
 
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
19
+ def relative_path(path)
34
20
  path.relative_path_from(base_dir)
21
+ rescue ArgumentError
22
+ path
35
23
  end
36
24
 
37
25
  def absolute_path(path)
@@ -69,6 +69,7 @@ module Steep
69
69
  end
70
70
  end
71
71
  end
72
+ thread.abort_on_exception = true
72
73
 
73
74
  Steep.logger.tagged "frontend" do
74
75
  begin
@@ -40,7 +40,7 @@ module Steep
40
40
 
41
41
  def collect_changes(request)
42
42
  push_buffer do |changes|
43
- path = project.relative_path(Pathname(URI.parse(request[:params][:textDocument][:uri]).path))
43
+ path = project.relative_path(Steep::PathHelper.to_pathname(request[:params][:textDocument][:uri]))
44
44
  version = request[:params][:textDocument][:version]
45
45
  Steep.logger.info { "Updating source: path=#{path}, version=#{version}..." }
46
46
 
@@ -53,8 +53,7 @@ module Steep
53
53
  when "textDocument/hover"
54
54
  id = request[:id]
55
55
 
56
- uri = URI.parse(request[:params][:textDocument][:uri])
57
- path = project.relative_path(Pathname(uri.path))
56
+ path = project.relative_path(Steep::PathHelper.to_pathname(request[:params][:textDocument][:uri]))
58
57
  line = request[:params][:position][:line]+1
59
58
  column = request[:params][:position][:character]
60
59
 
@@ -64,8 +63,7 @@ module Steep
64
63
  id = request[:id]
65
64
 
66
65
  params = request[:params]
67
- uri = URI.parse(params[:textDocument][:uri])
68
- path = project.relative_path(Pathname(uri.path))
66
+ path = project.relative_path(Steep::PathHelper.to_pathname(params[:textDocument][:uri]))
69
67
  line, column = params[:position].yield_self {|hash| [hash[:line]+1, hash[:character]] }
70
68
  trigger = params.dig(:context, :triggerCharacter)
71
69
 
@@ -144,7 +142,7 @@ module Steep
144
142
  decls = sig_service.files[relative_path].decls
145
143
  locator = RBS::Locator.new(decls: decls)
146
144
 
147
- hd, tail = locator.find2(line: job.line, column: job.column)
145
+ _hd, tail = locator.find2(line: job.line, column: job.column)
148
146
 
149
147
  namespace = []
150
148
  tail.each do |t|
@@ -21,9 +21,7 @@ module Steep
21
21
  end
22
22
 
23
23
  def uri(path)
24
- URI.parse(path.to_s).tap do |uri|
25
- uri.scheme = "file"
26
- end
24
+ Steep::PathHelper.to_uri(path)
27
25
  end
28
26
 
29
27
  def as_json(assignment:)
@@ -41,7 +39,7 @@ module Steep
41
39
  end
42
40
 
43
41
  def percentage
44
- checked_paths.size * 100 / total
42
+ checked_paths.size * 100 / all_paths.size
45
43
  end
46
44
 
47
45
  def all_paths
@@ -50,12 +48,7 @@ module Steep
50
48
 
51
49
  def checking_path?(path)
52
50
  [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
51
+ paths.include?(path)
59
52
  end
60
53
  end
61
54
 
@@ -463,7 +456,7 @@ module Steep
463
456
  end
464
457
 
465
458
  def pathname(uri)
466
- Pathname(URI.parse(uri).path)
459
+ Steep::PathHelper.to_pathname(uri)
467
460
  end
468
461
 
469
462
  def work_done_progress_supported?
@@ -524,40 +517,54 @@ module Steep
524
517
  end
525
518
 
526
519
  when "textDocument/didChange"
527
- broadcast_notification(message)
528
- path = pathname(message[:params][:textDocument][:uri])
529
- controller.push_changes(path)
520
+ if path = pathname(message[:params][:textDocument][:uri])
521
+ broadcast_notification(message)
522
+ controller.push_changes(path)
523
+ end
530
524
 
531
525
  when "textDocument/didSave"
532
- if typecheck_automatically
533
- if request = controller.make_request(last_request: current_type_check_request)
534
- start_type_check(
535
- request,
536
- last_request: current_type_check_request,
537
- start_progress: request.total > 10
538
- )
526
+ if path = pathname(message[:params][:textDocument][:uri])
527
+ if typecheck_automatically
528
+ if request = controller.make_request(last_request: current_type_check_request)
529
+ start_type_check(
530
+ request,
531
+ last_request: current_type_check_request,
532
+ start_progress: request.total > 10
533
+ )
534
+ end
539
535
  end
540
536
  end
541
537
 
542
538
  when "textDocument/didOpen"
543
- path = pathname(message[:params][:textDocument][:uri])
544
- controller.update_priority(open: path)
539
+ if path = pathname(message[:params][:textDocument][:uri])
540
+ controller.update_priority(open: path)
541
+ end
545
542
 
546
543
  when "textDocument/didClose"
547
- path = pathname(message[:params][:textDocument][:uri])
548
- controller.update_priority(close: path)
544
+ if path = pathname(message[:params][:textDocument][:uri])
545
+ controller.update_priority(close: path)
546
+ end
549
547
 
550
548
  when "textDocument/hover", "textDocument/completion"
551
549
  if interaction_worker
552
- result_controller << send_request(method: message[:method], params: message[:params], worker: interaction_worker) do |handler|
553
- handler.on_completion do |response|
554
- job_queue << SendMessageJob.to_client(
555
- message: {
556
- id: message[:id],
557
- result: response[:result]
558
- }
559
- )
550
+ if path = pathname(message[:params][:textDocument][:uri])
551
+ result_controller << send_request(method: message[:method], params: message[:params], worker: interaction_worker) do |handler|
552
+ handler.on_completion do |response|
553
+ job_queue << SendMessageJob.to_client(
554
+ message: {
555
+ id: message[:id],
556
+ result: response[:result]
557
+ }
558
+ )
559
+ end
560
560
  end
561
+ else
562
+ job_queue << SendMessageJob.to_client(
563
+ message: {
564
+ id: message[:id],
565
+ result: nil
566
+ }
567
+ )
561
568
  end
562
569
  end
563
570
 
@@ -594,20 +601,29 @@ module Steep
594
601
  end
595
602
 
596
603
  when "textDocument/definition", "textDocument/implementation"
597
- result_controller << group_request do |group|
598
- typecheck_workers.each do |worker|
599
- group << send_request(method: message[:method], params: message[:params], worker: worker)
600
- end
604
+ if path = pathname(message[:params][:textDocument][:uri])
605
+ result_controller << group_request do |group|
606
+ typecheck_workers.each do |worker|
607
+ group << send_request(method: message[:method], params: message[:params], worker: worker)
608
+ end
601
609
 
602
- group.on_completion do |handlers|
603
- links = handlers.flat_map(&:result)
604
- job_queue << SendMessageJob.to_client(
605
- message: {
606
- id: message[:id],
607
- result: links
608
- }
609
- )
610
+ group.on_completion do |handlers|
611
+ links = handlers.flat_map(&:result)
612
+ job_queue << SendMessageJob.to_client(
613
+ message: {
614
+ id: message[:id],
615
+ result: links
616
+ }
617
+ )
618
+ end
610
619
  end
620
+ else
621
+ job_queue << SendMessageJob.to_client(
622
+ message: {
623
+ id: message[:id],
624
+ result: []
625
+ }
626
+ )
611
627
  end
612
628
 
613
629
  when "$/typecheck"
@@ -86,10 +86,10 @@ module Steep
86
86
  queue << StartTypeCheckJob.new(guid: guid, changes: changes)
87
87
  end
88
88
 
89
- priority_paths = Set.new(params[:priority_uris].map {|uri| Pathname(URI.parse(uri).path) })
90
- library_paths = params[:library_uris].map {|uri| Pathname(URI.parse(uri).path) }
91
- signature_paths = params[:signature_uris].map {|uri| Pathname(URI.parse(uri).path) }
92
- code_paths = params[:code_uris].map {|uri| Pathname(URI.parse(uri).path) }
89
+ priority_paths = Set.new(params[:priority_uris].map {|uri| Steep::PathHelper.to_pathname(uri) })
90
+ library_paths = params[:library_uris].map {|uri| Steep::PathHelper.to_pathname(uri) }
91
+ signature_paths = params[:signature_uris].map {|uri| Steep::PathHelper.to_pathname(uri) }
92
+ code_paths = params[:code_uris].map {|uri| Steep::PathHelper.to_pathname(uri) }
93
93
 
94
94
  library_paths.each do |path|
95
95
  if priority_paths.include?(path)
@@ -135,21 +135,6 @@ 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
-
153
138
  case job
154
139
  when StartTypeCheckJob
155
140
  Steep.logger.info { "Processing StartTypeCheckJob for guid=#{job.guid}" }
@@ -164,7 +149,7 @@ module Steep
164
149
  writer.write(
165
150
  method: :"textDocument/publishDiagnostics",
166
151
  params: LSP::Interface::PublishDiagnosticsParams.new(
167
- uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
152
+ uri: Steep::PathHelper.to_uri(job.path),
168
153
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq
169
154
  )
170
155
  )
@@ -182,7 +167,7 @@ module Steep
182
167
  writer.write(
183
168
  method: :"textDocument/publishDiagnostics",
184
169
  params: LSP::Interface::PublishDiagnosticsParams.new(
185
- uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
170
+ uri: Steep::PathHelper.to_uri(job.path),
186
171
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
187
172
  )
188
173
  )
@@ -201,7 +186,7 @@ module Steep
201
186
  writer.write(
202
187
  method: :"textDocument/publishDiagnostics",
203
188
  params: LSP::Interface::PublishDiagnosticsParams.new(
204
- uri: URI.parse(job_path).tap {|uri| uri.scheme = "file"},
189
+ uri: Steep::PathHelper.to_uri(job.path),
205
190
  diagnostics: diagnostics.map {|diagnostic| formatter.format(diagnostic) }.uniq.compact
206
191
  )
207
192
  )
@@ -251,7 +236,7 @@ module Steep
251
236
  location: symbol.location.yield_self do |location|
252
237
  path = Pathname(location.buffer.name)
253
238
  {
254
- uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file" },
239
+ uri: Steep::PathHelper.to_uri(project.absolute_path(path)),
255
240
  range: {
256
241
  start: { line: location.start_line - 1, character: location.start_column },
257
242
  end: { line: location.end_line - 1, character: location.end_column }
@@ -279,7 +264,7 @@ module Steep
279
264
  end
280
265
 
281
266
  def goto(job)
282
- path = Pathname(URI.parse(job.params[:textDocument][:uri]).path)
267
+ path = Steep::PathHelper.to_pathname(job.params[:textDocument][:uri])
283
268
  line = job.params[:position][:line] + 1
284
269
  column = job.params[:position][:character]
285
270
 
@@ -306,7 +291,7 @@ module Steep
306
291
  path = project.absolute_path(path)
307
292
 
308
293
  {
309
- uri: URI.parse(path.to_s).tap {|uri| uri.scheme = "file" }.to_s,
294
+ uri: Steep::PathHelper.to_uri(path.to_s).to_s,
310
295
  range: loc.as_lsp_range
311
296
  }
312
297
  end
@@ -157,7 +157,7 @@ module Steep
157
157
  end
158
158
 
159
159
  def items_for_trigger(position:)
160
- node, *parents = source.find_nodes(line: position.line, column: position.column)
160
+ node, *_parents = source.find_nodes(line: position.line, column: position.column)
161
161
  node ||= source.node
162
162
 
163
163
  return [] unless node
@@ -265,7 +265,7 @@ module Steep
265
265
  def items_for_dot(position:)
266
266
  # foo. ←
267
267
  shift_pos = position-1
268
- node, *parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
268
+ node, *_parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
269
269
  node ||= source.node
270
270
 
271
271
  return [] unless node
@@ -355,7 +355,7 @@ module Steep
355
355
  end
356
356
  end
357
357
  end
358
- rescue RuntimeError => exn
358
+ rescue RuntimeError => _exn
359
359
  # nop
360
360
  end
361
361
 
@@ -35,7 +35,7 @@ module Steep
35
35
  def implementation(path:, line:, column:)
36
36
  locations = []
37
37
 
38
- relative_path = project.relative_path(path)
38
+ # relative_path = project.relative_path(path)
39
39
 
40
40
  queries = query_at(path: path, line: line, column: column)
41
41
  queries.uniq!
@@ -57,8 +57,6 @@ module Steep
57
57
  def definition(path:, line:, column:)
58
58
  locations = []
59
59
 
60
- relative_path = project.relative_path(path)
61
-
62
60
  queries = query_at(path: path, line: line, column: column)
63
61
  queries.uniq!
64
62
 
@@ -108,7 +106,7 @@ module Steep
108
106
  case
109
107
  when target = type_check.source_file?(relative_path)
110
108
  source = type_check.source_files[relative_path]
111
- typing, signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
109
+ typing, _signature = type_check_path(target: target, path: relative_path, content: source.content, line: line, column: column)
112
110
  if typing
113
111
  node, *parents = typing.source.find_nodes(line: line, column: column)
114
112
 
@@ -26,7 +26,7 @@ module Steep
26
26
  return if decls.nil?
27
27
 
28
28
  loc_key, path = ::RBS::Locator.new(decls: decls).find2(line: line, column: column) || return
29
- head, *tail = path
29
+ head, *_tail = path
30
30
 
31
31
  case head
32
32
  when ::RBS::Types::Alias
@@ -39,7 +39,6 @@ module Steep
39
39
  typed = 0
40
40
  untyped = 0
41
41
  errors = 0
42
- total = 0
43
42
  typing.method_calls.each_value do |call|
44
43
  case call
45
44
  when TypeInference::MethodCall::Typed
@@ -335,6 +335,9 @@ module Steep
335
335
  SourceFile.with_syntax_error(path: path, content: text, error: error)
336
336
  rescue EncodingError => exn
337
337
  SourceFile.no_data(path: path, content: "")
338
+ rescue RuntimeError => exn
339
+ Steep.log_error(exn)
340
+ SourceFile.no_data(path: path, content: text)
338
341
  end
339
342
 
340
343
  def self.type_check(source:, subtyping:)
data/lib/steep/source.rb CHANGED
@@ -39,7 +39,7 @@ module Steep
39
39
  end
40
40
 
41
41
  def self.new_parser
42
- ::Parser::Ruby30.new(Builder.new).tap do |parser|
42
+ ::Parser::Ruby31.new(Builder.new).tap do |parser|
43
43
  parser.diagnostics.all_errors_are_fatal = true
44
44
  parser.diagnostics.ignore_warnings = true
45
45
  end
@@ -421,9 +421,6 @@ module Steep
421
421
  when relation.sub_type.is_a?(AST::Types::Proc) && relation.super_type.is_a?(AST::Types::Proc)
422
422
  name = :__proc__
423
423
 
424
- sub_type = relation.sub_type
425
- super_type = relation.super_type
426
-
427
424
  All(relation) do |result|
428
425
  result.add(relation.map {|p| p.type }) do |rel|
429
426
  check_function(name, rel)
@@ -131,11 +131,13 @@ module Steep
131
131
 
132
132
  def for_new_method(method_name, node, args:, self_type:, definition:)
133
133
  annots = source.annotations(block: node, factory: checker.factory, context: nesting)
134
- type_env = TypeInference::TypeEnv.new(subtyping: checker, const_env: module_context&.const_env || self.type_env.const_env)
135
-
136
- self.type_env.const_types.each do |name, type|
137
- type_env.set(const: name, type: type)
138
- end
134
+ type_env = TypeInference::TypeEnv.build(
135
+ annotations: annots,
136
+ subtyping: checker,
137
+ const_env: module_context&.const_env || self.type_env.const_env,
138
+ signatures: checker.factory.env
139
+ )
140
+ type_env.merge_const_types(self.type_env)
139
141
 
140
142
  definition_method_type = if definition
141
143
  definition.methods[method_name]&.yield_self do |method|
@@ -320,7 +322,7 @@ module Steep
320
322
  instance_type: nil,
321
323
  module_type: nil,
322
324
  implement_name: nil,
323
- const_env: self.module_context.const_env,
325
+ const_env: const_env,
324
326
  class_name: self.module_context.class_name,
325
327
  module_definition: nil,
326
328
  instance_definition: nil
@@ -1355,24 +1357,23 @@ module Steep
1355
1357
  rhs = node.children[1]
1356
1358
 
1357
1359
  var_type = context.lvar_env[var]
1358
- node_type, constr = synthesize(rhs, hint: var_type)
1359
-
1360
- type = AST::Types::Union.build(types: [var_type, node_type])
1361
1360
 
1362
- constr_ = constr.update_lvar_env do |env|
1363
- env.assign(var, node: node, type: type) do |declared_type, type, result|
1361
+ if var_type
1362
+ type, constr = check(rhs, var_type) do |expected_type, actual_type, result|
1364
1363
  typing.add_error(
1365
1364
  Diagnostic::Ruby::IncompatibleAssignment.new(
1366
1365
  node: node,
1367
- lhs_type: declared_type,
1368
- rhs_type: type,
1366
+ lhs_type: expected_type,
1367
+ rhs_type: actual_type,
1369
1368
  result: result
1370
1369
  )
1371
1370
  )
1372
1371
  end
1372
+ else
1373
+ type, constr = synthesize(rhs)
1373
1374
  end
1374
1375
 
1375
- add_typing(node, type: type, constr: constr_)
1376
+ constr.add_typing(node, type: type)
1376
1377
  end
1377
1378
 
1378
1379
  when :restarg
@@ -1578,8 +1579,7 @@ module Steep
1578
1579
  message: "sclass receiver must be instance type or singleton type, but type given `#{type}`"
1579
1580
  )
1580
1581
  )
1581
- constr.add_typing(node, type: AST::Builtin.nil_type)
1582
- return
1582
+ return constr.add_typing(node, type: AST::Builtin.nil_type)
1583
1583
  end
1584
1584
 
1585
1585
  constructor.typing.add_context_for_node(node, context: constructor.context)
@@ -2299,7 +2299,8 @@ module Steep
2299
2299
  yield_self do
2300
2300
  value = node.children[0]
2301
2301
 
2302
- if hint.is_a?(AST::Types::Proc) && value.type == :sym
2302
+ case
2303
+ when hint.is_a?(AST::Types::Proc) && value && value.type == :sym
2303
2304
  if hint.one_arg?
2304
2305
  # Assumes Symbol#to_proc implementation
2305
2306
  param_type = hint.type.params.required[0]
@@ -2333,9 +2334,18 @@ module Steep
2333
2334
  else
2334
2335
  Steep.logger.error "Passing multiple args through Symbol#to_proc is not supported yet"
2335
2336
  end
2337
+ when value == nil
2338
+ type = AST::Types::Proc.new(
2339
+ type: method_context.method_type.block.type,
2340
+ location: nil,
2341
+ block: nil
2342
+ )
2343
+ if method_context.method_type.block.optional?
2344
+ type = AST::Types::Union.build(types: [type, AST::Builtin.nil_type])
2345
+ end
2336
2346
  end
2337
2347
 
2338
- type ||= synthesize(node.children[0], hint: hint).type
2348
+ type ||= synthesize(value, hint: hint).type
2339
2349
 
2340
2350
  add_typing node, type: type
2341
2351
  end
@@ -2827,13 +2837,6 @@ module Steep
2827
2837
 
2828
2838
  block_constr.typing.add_context_for_body(node, context: block_constr.context)
2829
2839
 
2830
- default_proc_function =
2831
- Interface::Function.new(
2832
- params: Interface::Function::Params.empty,
2833
- return_type: AST::Builtin.any_type,
2834
- location: nil
2835
- )
2836
-
2837
2840
  params.params.each do |param|
2838
2841
  _, block_constr = block_constr.synthesize(param.node, hint: param.type)
2839
2842
  end
@@ -3093,6 +3096,15 @@ module Steep
3093
3096
  private: private,
3094
3097
  self_type: AST::Types::Self.new)
3095
3098
 
3099
+ if send_node.type == :super || send_node.type == :zsuper
3100
+ method_name = method_context.name
3101
+ unless method_context.super_method
3102
+ return fallback_to_any(send_node) do
3103
+ Diagnostic::Ruby::UnexpectedSuper.new(node: send_node, method: method_name)
3104
+ end
3105
+ end
3106
+ end
3107
+
3096
3108
  constr.type_send_interface(node,
3097
3109
  interface: interface,
3098
3110
  receiver: receiver,
@@ -3144,6 +3156,9 @@ module Steep
3144
3156
  array_compact: Set[
3145
3157
  MethodName("::Array#compact"),
3146
3158
  MethodName("::Enumerable#compact")
3159
+ ],
3160
+ hash_compact: Set[
3161
+ MethodName("::Hash#compact")
3147
3162
  ]
3148
3163
  }
3149
3164
 
@@ -3170,6 +3185,29 @@ module Steep
3170
3185
  method_decls: decls
3171
3186
  )
3172
3187
 
3188
+ return [call, constr]
3189
+ end
3190
+ end
3191
+ when decl = decls.find {|decl| SPECIAL_METHOD_NAMES[:hash_compact].include?(decl.method_name) }
3192
+ if arguments.empty? && !block_params
3193
+ # compact
3194
+ return_type = method_type.type.return_type
3195
+ if AST::Builtin::Hash.instance_type?(return_type)
3196
+ key = return_type.args[0]
3197
+ value = return_type.args[1]
3198
+ type = AST::Builtin::Hash.instance_type(key, unwrap(value))
3199
+
3200
+ _, constr = add_typing(node, type: type)
3201
+ call = TypeInference::MethodCall::Special.new(
3202
+ node: node,
3203
+ context: constr.context.method_context,
3204
+ method_name: decl.method_name,
3205
+ receiver_type: receiver_type,
3206
+ actual_method_type: method_type.with(type: method_type.type.with(return_type: type)),
3207
+ return_type: type,
3208
+ method_decls: decls
3209
+ )
3210
+
3173
3211
  return [call, constr]
3174
3212
  end
3175
3213
  end
@@ -3309,7 +3347,6 @@ module Steep
3309
3347
  method_type = method_type.instantiate(instantiation)
3310
3348
 
3311
3349
  variance = Subtyping::VariableVariance.from_method_type(method_type)
3312
- occurence = Subtyping::VariableOccurence.from_method_type(method_type)
3313
3350
  constraints = Subtyping::Constraints.new(unknowns: type_params.map(&:name))
3314
3351
  ccontext = Subtyping::Constraints::Context.new(
3315
3352
  self_type: self_type,
@@ -132,9 +132,12 @@ module Steep
132
132
  end
133
133
  end
134
134
  when AST::Types::Logic::Not
135
- receiver, * = value_node.children
136
- receiver_type = typing.type_of(node: receiver)
137
- falsy_env, truthy_env = eval(env: env, type: receiver_type, node: receiver)
135
+ case value_node.type
136
+ when :send
137
+ receiver, * = value_node.children
138
+ receiver_type = typing.type_of(node: receiver)
139
+ falsy_env, truthy_env = eval(env: env, type: receiver_type, node: receiver)
140
+ end
138
141
  end
139
142
  else
140
143
  _, vars = decompose_value(node)
@@ -195,7 +198,6 @@ module Steep
195
198
 
196
199
  [truthy_types, falsy_types]
197
200
  else
198
- value_type = typing.type_of(node: value_node)
199
201
  types = [arg_type]
200
202
 
201
203
  case value_node.type
@@ -528,7 +528,6 @@ module Steep
528
528
  def each
529
529
  if block_given?
530
530
  errors = []
531
- positional_count = 0
532
531
 
533
532
  positional_arg.tap do |args|
534
533
  while (value, args = args.next())
@@ -615,7 +614,7 @@ module Steep
615
614
  end
616
615
  end
617
616
 
618
- pass = block_pass_arg
617
+ # pass = block_pass_arg
619
618
  # if pass.node
620
619
  # yield pass
621
620
  # end
@@ -73,6 +73,12 @@ module Steep
73
73
  end
74
74
  end
75
75
 
76
+ def merge_const_types(env)
77
+ env.const_types.each do |name, type|
78
+ const_types[name] = type unless const_types.key?(name)
79
+ end
80
+ end
81
+
76
82
  def set(const: nil, gvar: nil, ivar: nil, type:)
77
83
  case
78
84
  when const
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/steep.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "steep/version"
2
2
 
3
3
  require "pathname"
4
- require "parser/ruby30"
4
+ require "parser/ruby31"
5
5
  require "active_support"
6
6
  require "active_support/core_ext/object/try"
7
7
  require "active_support/core_ext/string/inflections"
@@ -21,6 +21,8 @@ require "terminal-table"
21
21
 
22
22
  require "rbs"
23
23
 
24
+ require "steep/path_helper"
25
+
24
26
  require "steep/shims/filter_map"
25
27
 
26
28
  require "steep/equatable"
@@ -65,7 +67,6 @@ require "steep/subtyping/cache"
65
67
  require "steep/subtyping/relation"
66
68
  require "steep/subtyping/constraints"
67
69
  require "steep/subtyping/variable_variance"
68
- require "steep/subtyping/variable_occurrence"
69
70
 
70
71
  require "steep/diagnostic/helper"
71
72
  require "steep/diagnostic/ruby"
@@ -0,0 +1,3 @@
1
+ module Mod
2
+ ->(){}
3
+ end
@@ -40,6 +40,18 @@
40
40
  | (::Numeric) -> ::Time
41
41
  | (::Time) -> ::Float
42
42
  code: Ruby::UnresolvedOverloading
43
+ - file: lambda.rb
44
+ diagnostics:
45
+ - range:
46
+ start:
47
+ line: 1
48
+ character: 7
49
+ end:
50
+ line: 1
51
+ character: 10
52
+ severity: ERROR
53
+ message: 'Cannot find the declaration of module: `Mod`'
54
+ code: Ruby::UnknownConstant
43
55
  - file: set_divide.rb
44
56
  diagnostics:
45
57
  - range:
data/steep.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.required_ruby_version = '>= 2.6.0'
30
30
 
31
- spec.add_runtime_dependency "parser", ">= 3.0"
31
+ spec.add_runtime_dependency "parser", ">= 3.1"
32
32
  spec.add_runtime_dependency "activesupport", ">= 5.1"
33
33
  spec.add_runtime_dependency "rainbow", ">= 2.2.2", "< 4.0"
34
34
  spec.add_runtime_dependency "listen", "~> 3.0"
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: 1.0.0
4
+ version: 1.0.1
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-05-20 00:00:00.000000000 Z
11
+ date: 2022-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
19
+ version: '3.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.0'
26
+ version: '3.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -149,6 +149,7 @@ extensions: []
149
149
  extra_rdoc_files: []
150
150
  files:
151
151
  - ".github/dependabot.yml"
152
+ - ".github/workflows/ruby-windows.yml"
152
153
  - ".github/workflows/ruby.yml"
153
154
  - ".gitignore"
154
155
  - ".gitmodules"
@@ -224,6 +225,7 @@ files:
224
225
  - lib/steep/interface/type_param.rb
225
226
  - lib/steep/method_name.rb
226
227
  - lib/steep/module_helper.rb
228
+ - lib/steep/path_helper.rb
227
229
  - lib/steep/project.rb
228
230
  - lib/steep/project/dsl.rb
229
231
  - lib/steep/project/options.rb
@@ -256,7 +258,6 @@ files:
256
258
  - lib/steep/subtyping/constraints.rb
257
259
  - lib/steep/subtyping/relation.rb
258
260
  - lib/steep/subtyping/result.rb
259
- - lib/steep/subtyping/variable_occurrence.rb
260
261
  - lib/steep/subtyping/variable_variance.rb
261
262
  - lib/steep/type_construction.rb
262
263
  - lib/steep/type_inference/block_params.rb
@@ -481,6 +482,7 @@ files:
481
482
  - smoke/regression/issue_332.rbs
482
483
  - smoke/regression/issue_372.rb
483
484
  - smoke/regression/issue_372.rbs
485
+ - smoke/regression/lambda.rb
484
486
  - smoke/regression/masgn.rb
485
487
  - smoke/regression/poly_new.rb
486
488
  - smoke/regression/poly_new.rbs
@@ -1,51 +0,0 @@
1
- module Steep
2
- module Subtyping
3
- class VariableOccurence
4
- attr_reader :params
5
- attr_reader :returns
6
-
7
- def initialize
8
- @params = Set.new
9
- @returns = Set.new
10
- end
11
-
12
- def add_method_type(method_type)
13
- method_type.type.params.each_type do |type|
14
- each_var(type) do |var|
15
- params << var
16
- end
17
- end
18
- each_var(method_type.type.return_type) do |var|
19
- returns << var
20
- end
21
-
22
- method_type.block&.yield_self do |block|
23
- block.type.params.each_type do |type|
24
- each_var(type) do |var|
25
- params << var
26
- end
27
- end
28
- each_var(block.type.return_type) do |var|
29
- returns << var
30
- end
31
- end
32
-
33
- params.subtract(returns)
34
- end
35
-
36
- def each_var(type, &block)
37
- type.free_variables.each(&block)
38
- end
39
-
40
- def strictly_return?(var)
41
- !params.member?(var) && returns.member?(var)
42
- end
43
-
44
- def self.from_method_type(method_type)
45
- self.new.tap do |occurence|
46
- occurence.add_method_type(method_type)
47
- end
48
- end
49
- end
50
- end
51
- end