steep 0.50.0 → 0.52.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +13 -6
  3. data/.github/workflows/ruby.yml +3 -1
  4. data/CHANGELOG.md +20 -1
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +9 -6
  7. data/lib/steep/ast/annotation/collection.rb +10 -8
  8. data/lib/steep/ast/types/factory.rb +7 -6
  9. data/lib/steep/cli.rb +87 -1
  10. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  11. data/lib/steep/diagnostic/ruby.rb +21 -15
  12. data/lib/steep/index/source_index.rb +18 -1
  13. data/lib/steep/module_helper.rb +4 -7
  14. data/lib/steep/server/interaction_worker.rb +32 -135
  15. data/lib/steep/server/lsp_formatter.rb +234 -0
  16. data/lib/steep/server/master.rb +1 -1
  17. data/lib/steep/services/completion_provider.rb +109 -1
  18. data/lib/steep/services/goto_service.rb +4 -6
  19. data/lib/steep/services/hover_provider/rbs.rb +63 -0
  20. data/lib/steep/services/hover_provider/ruby.rb +168 -0
  21. data/lib/steep/services/hover_provider/singleton_methods.rb +21 -0
  22. data/lib/steep/services/type_check_service.rb +6 -3
  23. data/lib/steep/source.rb +71 -18
  24. data/lib/steep/subtyping/check.rb +2 -2
  25. data/lib/steep/type_construction.rb +183 -148
  26. data/lib/steep/type_inference/constant_env.rb +33 -15
  27. data/lib/steep/type_inference/context.rb +10 -7
  28. data/lib/steep/type_inference/type_env.rb +16 -54
  29. data/lib/steep/version.rb +1 -1
  30. data/lib/steep.rb +11 -8
  31. data/smoke/const/test_expectations.yml +24 -19
  32. data/smoke/diagnostics/test_expectations.yml +77 -17
  33. data/smoke/integer/test_expectations.yml +24 -4
  34. data/smoke/method/test_expectations.yml +30 -0
  35. data/smoke/regression/test_expectations.yml +24 -0
  36. data/smoke/yield/test_expectations.yml +20 -0
  37. data/steep.gemspec +1 -1
  38. metadata +9 -5
  39. data/lib/steep/services/hover_content.rb +0 -206
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ff552f6d8f112693dc9969f20d851ddbc6d7d2f16cebd45f802a5d703618b58
4
- data.tar.gz: 723464fe34c81c3e83b66338dfc6fd3e7c3da2aaf30c1fc48116c7de794ca961
3
+ metadata.gz: c373c6a4366397b2b014850a8993ce6d8a0c27bcdd8bc8c5135bb64b4132b162
4
+ data.tar.gz: '097245d5e3745ceb634a2f2bcd007cca0c549449c835b644feb62c23c3c6e3ed'
5
5
  SHA512:
6
- metadata.gz: bf9f83791cfb636c65a26f404fb164fa7fc3906376e36a79a1ca4ae7eeab8020e23c5b9ce5e649a38ed79080d0aa2c00fe53a5cce8a1c76c259237f38880bbf2
7
- data.tar.gz: 25bd9e11db283acdfc957682860e674d544b82be1b5a68bc87ca96888907966b721301c937cdaa3e06bf0226ec82fa8c78eacfaf4987637a601f75ca8d8e950c
6
+ metadata.gz: eadc49c7c81bce14e4e4c5f9adbcb0411669cd1f78eb290029953e26ea3997d15a65db53329124812e841f3fbe486d30f25bbf89d7bce74d528692176250c795
7
+ data.tar.gz: 3257fa14e2888e68afd6266e1c1d28dc95b8ef7e1ec499393994f35cb715abfc133f005d0ce79962fc6642f7fdeb0b28c35fa0d3bb5687972ea1b31d0a9d6c38
@@ -1,8 +1,15 @@
1
1
  version: 2
2
+
2
3
  updates:
3
- - package-ecosystem: bundler
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- time: "20:00"
8
- open-pull-requests-limit: 10
4
+ - package-ecosystem: bundler
5
+ directory: "/"
6
+ schedule:
7
+ interval: daily
8
+ time: "20:00"
9
+ open-pull-requests-limit: 10
10
+
11
+ - package-ecosystem: "github-actions"
12
+ directory: "/"
13
+ schedule:
14
+ # Check for updates to GitHub Actions every weekday
15
+ interval: "daily"
@@ -14,6 +14,7 @@ jobs:
14
14
  container_tag:
15
15
  - "2.7"
16
16
  - "3.0"
17
+ - "3.1"
17
18
  - "master-nightly-focal"
18
19
  task:
19
20
  - test
@@ -22,9 +23,10 @@ jobs:
22
23
  container:
23
24
  image: rubylang/ruby:${{ matrix.container_tag }}
24
25
  steps:
25
- - uses: actions/checkout@v1
26
+ - uses: actions/checkout@v3
26
27
  - name: Run test
27
28
  run: |
29
+ git config --global --add safe.directory /__w/steep/steep
28
30
  ruby -v
29
31
  gem install bundler
30
32
  bundle install --jobs 4 --retry 3
data/CHANGELOG.md CHANGED
@@ -2,7 +2,26 @@
2
2
 
3
3
  ## master
4
4
 
5
- # 0.50.0 (2022-03-22)
5
+ ## 0.52.1 (2022-04-25)
6
+
7
+ * Better union type inference (it type checks `Array#filter_map` now!) ([\#531](https://github.com/soutaro/steep/pull/531))
8
+ * Improve method call hover message ([\#537](https://github.com/soutaro/steep/pull/537), [\#538](https://github.com/soutaro/steep/pull/538))
9
+ * Make `NilClass#!` a special method to improve flow-sensitive typing ([\#539](https://github.com/soutaro/steep/pull/539))
10
+ * Fix `steep binstub` ([\#540](https://github.com/soutaro/steep/pull/540), [\#541](https://github.com/soutaro/steep/pull/541))
11
+
12
+ ## 0.52.0 (2022-04-05)
13
+
14
+ * Add `steep binstub` command ([\#527](https://github.com/soutaro/steep/pull/527))
15
+ * Let hover and completion work in heredoc ([\#528](https://github.com/soutaro/steep/pull/528))
16
+ * Better constant typing ([\#529](https://github.com/soutaro/steep/pull/529))
17
+
18
+ ## 0.51.0 (2022-04-01)
19
+
20
+ * Completion for constant ([\#524](https://github.com/soutaro/steep/pull/524))
21
+ * Better hover/completion message ([\#525](https://github.com/soutaro/steep/pull/525))
22
+ * Show available commands when using `--help` ([\#523](https://github.com/soutaro/steep/pull/523))
23
+
24
+ ## 0.50.0 (2022-03-22)
6
25
 
7
26
  * CLI option for override steep command at spawn worker ([\#511](https://github.com/soutaro/steep/pull/511))
8
27
  * LSP related improvements for Sublime LSP ([\#513](https://github.com/soutaro/steep/pull/513))
data/Gemfile CHANGED
@@ -12,3 +12,4 @@ gem "minitest-hooks"
12
12
  group :stackprof, optional: true do
13
13
  gem "stackprof"
14
14
  end
15
+ gem 'minitest-slow_test'
data/Gemfile.lock CHANGED
@@ -1,14 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (0.50.0)
4
+ steep (0.52.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
9
  parser (>= 3.0)
10
10
  rainbow (>= 2.2.2, < 4.0)
11
- rbs (>= 2.2.0)
11
+ rbs (>= 2.3.2)
12
12
  terminal-table (>= 2, < 4)
13
13
 
14
14
  PATH
@@ -30,7 +30,7 @@ GEM
30
30
  minitest (>= 5.1)
31
31
  tzinfo (~> 2.0)
32
32
  ast (2.4.2)
33
- concurrent-ruby (1.1.9)
33
+ concurrent-ruby (1.1.10)
34
34
  ffi (1.15.5)
35
35
  i18n (1.10.0)
36
36
  concurrent-ruby (~> 1.0)
@@ -41,15 +41,17 @@ GEM
41
41
  minitest (5.15.0)
42
42
  minitest-hooks (1.5.0)
43
43
  minitest (> 5.3)
44
- parallel (1.22.0)
45
- parser (3.1.1.0)
44
+ minitest-slow_test (0.2.0)
45
+ minitest (>= 5.0)
46
+ parallel (1.22.1)
47
+ parser (3.1.2.0)
46
48
  ast (~> 2.4.1)
47
49
  rainbow (3.1.1)
48
50
  rake (13.0.6)
49
51
  rb-fsevent (0.11.1)
50
52
  rb-inotify (0.10.1)
51
53
  ffi (~> 1.0)
52
- rbs (2.2.2)
54
+ rbs (2.3.2)
53
55
  stackprof (0.2.19)
54
56
  terminal-table (3.0.2)
55
57
  unicode-display_width (>= 1.1.1, < 3)
@@ -63,6 +65,7 @@ PLATFORMS
63
65
  DEPENDENCIES
64
66
  minitest (~> 5.15)
65
67
  minitest-hooks
68
+ minitest-slow_test
66
69
  rake
67
70
  stackprof
68
71
  steep!
@@ -4,7 +4,7 @@ module Steep
4
4
  class Collection
5
5
  attr_reader :annotations
6
6
  attr_reader :factory
7
- attr_reader :current_module
7
+ attr_reader :context
8
8
 
9
9
  attr_reader :var_type_annotations
10
10
  attr_reader :const_type_annotations
@@ -19,10 +19,10 @@ module Steep
19
19
  attr_reader :dynamic_annotations
20
20
  attr_reader :break_type_annotation
21
21
 
22
- def initialize(annotations:, factory:, current_module:)
22
+ def initialize(annotations:, factory:, context:)
23
23
  @annotations = annotations
24
24
  @factory = factory
25
- @current_module = current_module
25
+ @context = context
26
26
 
27
27
  @var_type_annotations = {}
28
28
  @method_type_annotations = {}
@@ -64,7 +64,7 @@ module Steep
64
64
 
65
65
  def absolute_type(type)
66
66
  if type
67
- factory.absolute_type(type, namespace: current_module)
67
+ factory.absolute_type(type, context: context)
68
68
  end
69
69
  end
70
70
 
@@ -140,7 +140,7 @@ module Steep
140
140
  end
141
141
 
142
142
  def merge_block_annotations(annotations)
143
- if annotations.current_module != current_module || annotations.factory != factory
143
+ if annotations.context != context || annotations.factory != factory
144
144
  raise "Cannot merge another annotation: self=#{self}, other=#{annotations}"
145
145
  end
146
146
 
@@ -148,9 +148,11 @@ module Steep
148
148
  annotation.is_a?(BlockType) || annotation.is_a?(BreakType)
149
149
  end
150
150
 
151
- self.class.new(annotations: retained_annotations + annotations.annotations,
152
- factory: factory,
153
- current_module: current_module)
151
+ self.class.new(
152
+ annotations: retained_annotations + annotations.annotations,
153
+ factory: factory,
154
+ context: context
155
+ )
154
156
  end
155
157
 
156
158
  def any?(&block)
@@ -18,7 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def type_name_resolver
21
- @type_name_resolver ||= RBS::TypeNameResolver.from_env(definition_builder.env)
21
+ @type_name_resolver ||= RBS::Resolver::TypeNameResolver.new(definition_builder.env)
22
22
  end
23
23
 
24
24
  def type_opt(type)
@@ -431,7 +431,8 @@ module Steep
431
431
  case defined_in
432
432
  when RBS::BuiltinNames::BasicObject.name,
433
433
  RBS::BuiltinNames::TrueClass.name,
434
- RBS::BuiltinNames::FalseClass.name
434
+ RBS::BuiltinNames::FalseClass.name,
435
+ AST::Builtin::NilClass.module_name
435
436
  return method_type.with(
436
437
  type: method_type.type.with(
437
438
  return_type: AST::Types::Logic::Not.new(location: method_type.type.return_type.location)
@@ -810,15 +811,15 @@ module Steep
810
811
  @env ||= definition_builder.env
811
812
  end
812
813
 
813
- def absolute_type(type, namespace:)
814
+ def absolute_type(type, context:)
814
815
  absolute_type = type_1(type).map_type_name do |name|
815
- absolute_type_name(name, namespace: namespace) || name.absolute!
816
+ absolute_type_name(name, context: context) || name.absolute!
816
817
  end
817
818
  type(absolute_type)
818
819
  end
819
820
 
820
- def absolute_type_name(type_name, namespace:)
821
- type_name_resolver.resolve(type_name, context: namespace.ascend)
821
+ def absolute_type_name(type_name, context:)
822
+ type_name_resolver.resolve(type_name, context: context)
822
823
  end
823
824
 
824
825
  def instance_type(type_name, args: nil, location: nil)
data/lib/steep/cli.rb CHANGED
@@ -18,11 +18,19 @@ module Steep
18
18
  end
19
19
 
20
20
  def self.available_commands
21
- [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats]
21
+ [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats, :binstub]
22
22
  end
23
23
 
24
24
  def process_global_options
25
25
  OptionParser.new do |opts|
26
+ opts.banner = <<~USAGE
27
+ Usage: steep [options]
28
+
29
+ available commands: #{CLI.available_commands.join(', ')}
30
+
31
+ Options:
32
+ USAGE
33
+
26
34
  opts.on("--version") do
27
35
  process_version
28
36
  exit 0
@@ -200,6 +208,84 @@ module Steep
200
208
  end.run
201
209
  end
202
210
 
211
+ def process_binstub
212
+ path = Pathname("bin/steep")
213
+ root_path = Pathname.pwd
214
+ force = false
215
+
216
+ OptionParser.new do |opts|
217
+ opts.banner = <<BANNER
218
+ Usage: steep binstub [options]
219
+
220
+ Generate a binstub to execute Steep with setting up Bundler and rbenv/rvm.
221
+ Use the executable for LSP integration setup.
222
+
223
+ Options:
224
+ BANNER
225
+ handle_logging_options opts
226
+
227
+ opts.on("-o PATH", "--output=PATH", "The path of the executable file (defaults to `bin/steep`)") do |v|
228
+ path = Pathname(v)
229
+ end
230
+
231
+ opts.on("--root=PATH", "The repository root path (defaults to `.`)") do |v|
232
+ root_path = (Pathname.pwd + v).cleanpath
233
+ end
234
+
235
+ opts.on("--[no-]force", "Overwrite file (defaults to false)") do
236
+ force = true
237
+ end
238
+ end.parse!(argv)
239
+
240
+ binstub_path = (Pathname.pwd + path).cleanpath
241
+ bindir_path = binstub_path.parent
242
+
243
+ bindir_path.mkpath
244
+
245
+ gemfile_path =
246
+ if defined?(Bundler)
247
+ Bundler.default_gemfile.relative_path_from(bindir_path)
248
+ else
249
+ Pathname("../Gemfile")
250
+ end
251
+
252
+ if binstub_path.file?
253
+ if force
254
+ stdout.puts Rainbow("#{path} already exists. Overwriting...").yellow
255
+ else
256
+ stdout.puts Rainbow(''"⚠️ #{path} already exists. Bye! 👋").red
257
+ return 0
258
+ end
259
+ end
260
+
261
+ template = <<TEMPLATE
262
+ #!/usr/bin/env bash
263
+
264
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
265
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/#{gemfile_path})
266
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/#{root_path.relative_path_from(bindir_path)})
267
+
268
+ STEEP="bundle exec --gemfile=${GEMFILE} steep"
269
+
270
+ if type "rbenv" > /dev/null 2>&1; then
271
+ STEEP="rbenv exec ${STEEP}"
272
+ else
273
+ if type "rvm" > /dev/null 2>&1; then
274
+ STEEP="rvm ${ROOT_DIR} do ${STEEP}"
275
+ fi
276
+ fi
277
+
278
+ exec $STEEP $@
279
+ TEMPLATE
280
+
281
+ binstub_path.write(template)
282
+ binstub_path.chmod(0755)
283
+
284
+ stdout.puts Rainbow("Successfully generated executable #{path} 🎉").blue
285
+
286
+ 0
287
+ end
288
+
203
289
  def process_version
204
290
  stdout.puts Steep::VERSION
205
291
  0
@@ -0,0 +1,28 @@
1
+ Steep.logger.error "Diagnostic `Ruby::UnknownConstantAssigned` is deprecated. Use `Ruby::UnknownConstant` instead."
2
+
3
+ module Steep
4
+ module Diagnostic
5
+ module Ruby
6
+ class UnknownConstantAssigned < Base
7
+ attr_reader :context
8
+ attr_reader :name
9
+
10
+ def initialize(node:, context:, name:)
11
+ const = node.children[0]
12
+ loc = if const
13
+ const.loc.expression.join(node.loc.name)
14
+ else
15
+ node.loc.name
16
+ end
17
+ super(node: node, location: loc)
18
+ @context = context
19
+ @name = name
20
+ end
21
+
22
+ def header_line
23
+ "Cannot find the declaration of constant `#{name}`"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -551,27 +551,33 @@ module Steep
551
551
  end
552
552
  end
553
553
 
554
- class UnknownConstantAssigned < Base
555
- attr_reader :context
554
+ class UnknownConstant < Base
556
555
  attr_reader :name
556
+ attr_reader :kind
557
557
 
558
- def initialize(node:, context:, name:)
559
- const = node.children[0]
560
- loc = if const
561
- const.loc.expression.join(node.loc.name)
562
- else
563
- node.loc.name
564
- end
565
- super(node: node, location: loc)
566
- @context = context
558
+ def initialize(node:, name:)
559
+ super(node: node, location: node.loc.name)
567
560
  @name = name
561
+ @kind = :constant
562
+ end
563
+
564
+ def class!
565
+ @kind = :class
566
+ self
567
+ end
568
+
569
+ def module!
570
+ @kind = :module
571
+ self
568
572
  end
569
573
 
570
574
  def header_line
571
- "Cannot find the declaration of constant `#{name}`"
575
+ "Cannot find the declaration of #{kind}: `#{name}`"
572
576
  end
573
577
  end
574
578
 
579
+ autoload :UnknownConstantAssigned, "steep/diagnostic/deprecated/unknown_constant_assigned"
580
+
575
581
  class FallbackAny < Base
576
582
  def initialize(node:)
577
583
  super(node: node)
@@ -744,7 +750,7 @@ module Steep
744
750
  ImplicitBreakValueMismatch => :warning,
745
751
  FallbackAny => :information,
746
752
  ElseOnExhaustiveCase => :warning,
747
- UnknownConstantAssigned => :warning,
753
+ UnknownConstant => :warning,
748
754
  MethodDefinitionMissing => :information
749
755
  }
750
756
  ).freeze
@@ -757,7 +763,7 @@ module Steep
757
763
  ImplicitBreakValueMismatch => nil,
758
764
  FallbackAny => nil,
759
765
  ElseOnExhaustiveCase => nil,
760
- UnknownConstantAssigned => nil,
766
+ UnknownConstant => nil,
761
767
  MethodDefinitionMissing => nil
762
768
  }
763
769
  ).freeze
@@ -770,7 +776,7 @@ module Steep
770
776
  ImplicitBreakValueMismatch => nil,
771
777
  FallbackAny => nil,
772
778
  ElseOnExhaustiveCase => nil,
773
- UnknownConstantAssigned => nil,
779
+ UnknownConstant => nil,
774
780
  MethodDefinitionMissing => nil,
775
781
  UnexpectedJump => nil
776
782
  }
@@ -16,7 +16,7 @@ module Steep
16
16
 
17
17
  def add_definition(node)
18
18
  case node.type
19
- when :casgn, :class, :module
19
+ when :casgn, :const
20
20
  @definitions << node
21
21
  else
22
22
  raise "Unexpected constant definition: #{node.type}"
@@ -145,6 +145,23 @@ module Steep
145
145
  raise
146
146
  end
147
147
  end
148
+
149
+ def reference(constant_node: nil)
150
+ case
151
+ when constant_node
152
+ constant_index.each do |name, entry|
153
+ if entry.references.include?(constant_node)
154
+ return name
155
+ end
156
+
157
+ if entry.definitions.include?(constant_node)
158
+ return name
159
+ end
160
+ end
161
+
162
+ nil
163
+ end
164
+ end
148
165
  end
149
166
  end
150
167
  end
@@ -1,12 +1,9 @@
1
1
  module Steep
2
2
  module ModuleHelper
3
- def module_name_from_node(node)
4
- case node.type
5
- when :const, :casgn
6
- namespace = namespace_from_node(node.children[0]) or return
7
- name = node.children[1]
8
- RBS::TypeName.new(name: name, namespace: namespace)
9
- end
3
+ def module_name_from_node(parent_node, constant_name)
4
+ namespace = namespace_from_node(parent_node) or return
5
+ name = constant_name
6
+ RBS::TypeName.new(name: name, namespace: namespace)
10
7
  end
11
8
 
12
9
  def namespace_from_node(node)