steep 1.0.0 → 1.0.1

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: 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