steep 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -1
  3. data/CHANGELOG.md +6 -0
  4. data/Rakefile +5 -2
  5. data/bin/output_rebaseline.rb +49 -0
  6. data/bin/output_test.rb +93 -0
  7. data/lib/steep.rb +8 -3
  8. data/lib/steep/cli.rb +1 -1
  9. data/lib/steep/diagnostic/helper.rb +17 -0
  10. data/lib/steep/diagnostic/lsp_formatter.rb +16 -0
  11. data/lib/steep/diagnostic/ruby.rb +623 -0
  12. data/lib/steep/diagnostic/signature.rb +224 -0
  13. data/lib/steep/drivers/annotations.rb +13 -6
  14. data/lib/steep/drivers/check.rb +83 -60
  15. data/lib/steep/drivers/diagnostic_printer.rb +94 -0
  16. data/lib/steep/drivers/stats.rb +125 -29
  17. data/lib/steep/drivers/trace_printer.rb +5 -1
  18. data/lib/steep/drivers/validate.rb +13 -6
  19. data/lib/steep/drivers/watch.rb +26 -9
  20. data/lib/steep/drivers/worker.rb +5 -0
  21. data/lib/steep/project/options.rb +4 -4
  22. data/lib/steep/project/signature_file.rb +8 -2
  23. data/lib/steep/project/stats_calculator.rb +80 -0
  24. data/lib/steep/project/target.rb +64 -53
  25. data/lib/steep/range_extension.rb +29 -0
  26. data/lib/steep/server/base_worker.rb +42 -4
  27. data/lib/steep/server/code_worker.rb +37 -24
  28. data/lib/steep/server/interaction_worker.rb +1 -0
  29. data/lib/steep/server/master.rb +268 -82
  30. data/lib/steep/server/signature_worker.rb +7 -59
  31. data/lib/steep/server/worker_process.rb +9 -9
  32. data/lib/steep/signature/validator.rb +33 -9
  33. data/lib/steep/type_construction.rb +276 -194
  34. data/lib/steep/version.rb +1 -1
  35. data/smoke/alias/a.rb +0 -3
  36. data/smoke/alias/b.rb +0 -1
  37. data/smoke/alias/c.rb +0 -2
  38. data/smoke/alias/test.yaml +73 -0
  39. data/smoke/and/a.rb +0 -3
  40. data/smoke/and/test.yaml +24 -0
  41. data/smoke/array/a.rb +0 -3
  42. data/smoke/array/b.rb +0 -2
  43. data/smoke/array/c.rb +0 -1
  44. data/smoke/array/test.yaml +80 -0
  45. data/smoke/block/a.rb +0 -2
  46. data/smoke/block/b.rb +0 -2
  47. data/smoke/block/d.rb +0 -4
  48. data/smoke/block/test.yaml +96 -0
  49. data/smoke/broken/Steepfile +5 -0
  50. data/smoke/broken/broken.rb +0 -0
  51. data/smoke/broken/broken.rbs +0 -0
  52. data/smoke/broken/test.yaml +6 -0
  53. data/smoke/case/a.rb +0 -3
  54. data/smoke/case/test.yaml +36 -0
  55. data/smoke/class/a.rb +0 -3
  56. data/smoke/class/c.rb +0 -1
  57. data/smoke/class/f.rb +0 -1
  58. data/smoke/class/g.rb +0 -2
  59. data/smoke/class/i.rb +0 -2
  60. data/smoke/class/test.yaml +89 -0
  61. data/smoke/const/a.rb +0 -3
  62. data/smoke/const/b.rb +7 -0
  63. data/smoke/const/b.rbs +5 -0
  64. data/smoke/const/test.yaml +96 -0
  65. data/smoke/diagnostics-rbs-duplicated/Steepfile +5 -0
  66. data/smoke/diagnostics-rbs-duplicated/a.rbs +5 -0
  67. data/smoke/diagnostics-rbs-duplicated/test.yaml +10 -0
  68. data/smoke/diagnostics-rbs/Steepfile +5 -0
  69. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +20 -0
  70. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +7 -0
  71. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +3 -0
  72. data/smoke/diagnostics-rbs/invalid-type-application.rbs +7 -0
  73. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +3 -0
  74. data/smoke/diagnostics-rbs/recursive-alias.rbs +5 -0
  75. data/smoke/diagnostics-rbs/recursive-class.rbs +8 -0
  76. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +7 -0
  77. data/smoke/diagnostics-rbs/test.yaml +142 -0
  78. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +3 -0
  79. data/smoke/diagnostics-rbs/unknown-type-name.rbs +13 -0
  80. data/smoke/diagnostics/Steepfile +5 -0
  81. data/smoke/diagnostics/a.rbs +26 -0
  82. data/smoke/diagnostics/argument_type_mismatch.rb +1 -0
  83. data/smoke/diagnostics/block_body_type_mismatch.rb +1 -0
  84. data/smoke/diagnostics/block_type_mismatch.rb +3 -0
  85. data/smoke/diagnostics/break_type_mismatch.rb +1 -0
  86. data/smoke/diagnostics/else_on_exhaustive_case.rb +12 -0
  87. data/smoke/diagnostics/incompatible_annotation.rb +6 -0
  88. data/smoke/diagnostics/incompatible_argument.rb +1 -0
  89. data/smoke/diagnostics/incompatible_assignment.rb +8 -0
  90. data/smoke/diagnostics/method_arity_mismatch.rb +11 -0
  91. data/smoke/diagnostics/method_body_type_mismatch.rb +6 -0
  92. data/smoke/diagnostics/method_definition_missing.rb +2 -0
  93. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +7 -0
  94. data/smoke/diagnostics/missing_keyword.rb +1 -0
  95. data/smoke/diagnostics/no_method.rb +1 -0
  96. data/smoke/diagnostics/required_block_missing.rb +1 -0
  97. data/smoke/diagnostics/return_type_mismatch.rb +6 -0
  98. data/smoke/diagnostics/test.yaml +333 -0
  99. data/smoke/diagnostics/unexpected_block_given.rb +1 -0
  100. data/smoke/diagnostics/unexpected_dynamic_method.rb +3 -0
  101. data/smoke/diagnostics/unexpected_jump.rb +4 -0
  102. data/smoke/diagnostics/unexpected_jump_value.rb +3 -0
  103. data/smoke/diagnostics/unexpected_keyword.rb +1 -0
  104. data/smoke/diagnostics/unexpected_splat.rb +1 -0
  105. data/smoke/diagnostics/unexpected_yield.rb +6 -0
  106. data/smoke/diagnostics/unknown_constant_assigned.rb +7 -0
  107. data/smoke/diagnostics/unresolved_overloading.rb +1 -0
  108. data/smoke/diagnostics/unsatisfiable_constraint.rb +7 -0
  109. data/smoke/diagnostics/unsupported_syntax.rb +2 -0
  110. data/smoke/dstr/a.rb +0 -1
  111. data/smoke/dstr/test.yaml +10 -0
  112. data/smoke/ensure/a.rb +0 -4
  113. data/smoke/ensure/test.yaml +47 -0
  114. data/smoke/enumerator/a.rb +0 -6
  115. data/smoke/enumerator/b.rb +0 -3
  116. data/smoke/enumerator/test.yaml +100 -0
  117. data/smoke/extension/a.rb +0 -1
  118. data/smoke/extension/b.rb +0 -2
  119. data/smoke/extension/c.rb +0 -1
  120. data/smoke/extension/test.yaml +50 -0
  121. data/smoke/hash/b.rb +0 -1
  122. data/smoke/hash/c.rb +0 -3
  123. data/smoke/hash/d.rb +0 -1
  124. data/smoke/hash/e.rb +0 -1
  125. data/smoke/hash/test.yaml +62 -0
  126. data/smoke/hello/hello.rb +0 -2
  127. data/smoke/hello/test.yaml +18 -0
  128. data/smoke/if/a.rb +0 -2
  129. data/smoke/if/test.yaml +27 -0
  130. data/smoke/implements/a.rb +0 -2
  131. data/smoke/implements/test.yaml +16 -0
  132. data/smoke/initialize/test.yaml +4 -0
  133. data/smoke/integer/a.rb +0 -7
  134. data/smoke/integer/test.yaml +66 -0
  135. data/smoke/interface/a.rb +0 -2
  136. data/smoke/interface/test.yaml +16 -0
  137. data/smoke/kwbegin/a.rb +0 -1
  138. data/smoke/kwbegin/test.yaml +14 -0
  139. data/smoke/lambda/a.rb +1 -4
  140. data/smoke/lambda/test.yaml +28 -0
  141. data/smoke/literal/a.rb +0 -5
  142. data/smoke/literal/b.rb +0 -2
  143. data/smoke/literal/test.yaml +79 -0
  144. data/smoke/map/test.yaml +4 -0
  145. data/smoke/method/a.rb +0 -5
  146. data/smoke/method/b.rb +0 -1
  147. data/smoke/method/test.yaml +71 -0
  148. data/smoke/module/a.rb +0 -2
  149. data/smoke/module/b.rb +0 -2
  150. data/smoke/module/c.rb +0 -1
  151. data/smoke/module/d.rb +0 -1
  152. data/smoke/module/f.rb +0 -2
  153. data/smoke/module/test.yaml +51 -0
  154. data/smoke/regexp/a.rb +0 -38
  155. data/smoke/regexp/b.rb +0 -26
  156. data/smoke/regexp/test.yaml +372 -0
  157. data/smoke/regression/set_divide.rb +0 -4
  158. data/smoke/regression/test.yaml +38 -0
  159. data/smoke/rescue/a.rb +0 -5
  160. data/smoke/rescue/test.yaml +60 -0
  161. data/smoke/self/a.rb +0 -2
  162. data/smoke/self/test.yaml +16 -0
  163. data/smoke/skip/skip.rb +0 -2
  164. data/smoke/skip/test.yaml +16 -0
  165. data/smoke/stdout/test.yaml +4 -0
  166. data/smoke/super/a.rb +0 -4
  167. data/smoke/super/test.yaml +52 -0
  168. data/smoke/toplevel/a.rb +0 -1
  169. data/smoke/toplevel/test.yaml +12 -0
  170. data/smoke/tsort/a.rb +0 -3
  171. data/smoke/tsort/test.yaml +32 -0
  172. data/smoke/type_case/a.rb +0 -4
  173. data/smoke/type_case/test.yaml +33 -0
  174. data/smoke/yield/a.rb +0 -3
  175. data/smoke/yield/b.rb +6 -0
  176. data/smoke/yield/test.yaml +49 -0
  177. data/steep.gemspec +3 -3
  178. metadata +108 -17
  179. data/bin/smoke_runner.rb +0 -139
  180. data/lib/steep/drivers/signature_error_printer.rb +0 -25
  181. data/lib/steep/errors.rb +0 -594
  182. data/lib/steep/signature/errors.rb +0 -128
  183. data/lib/steep/type_assignability.rb +0 -367
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33c89b6005654ebdc947af336b1c7fa47e02c45c480d27b9f4ef0ab6d3403dd1
4
- data.tar.gz: be7b7812e1ba321b91680884fccc55fd57dd5836c89bf7817624b72706490d6e
3
+ metadata.gz: 725a6432404ca94fa643ba89ed2028d7b3923ece4031512ebea88ebbdc4fd01e
4
+ data.tar.gz: 957a95acbda38474f1a796f829f8cf098b69484a8ddf42bd39e2c2be935a961e
5
5
  SHA512:
6
- metadata.gz: 36945eb5fe4a04061c87bc76488c65ef2b009fcdb8279fd542a1249a10a223cd9571371f333e4ec29870a66dd120c4d9e65d572a9d3c90d56dba5c2c1dd95761
7
- data.tar.gz: d9c2e56878f06d71edeb9a10b275c19afd7ba42b1af16ef01ef890ca3791ad4f92f903f7679fcf77b310638b0d0f5fced596e094ec225ff2f567e0f063bfc527
6
+ metadata.gz: 363a04610a81d4652541c9e5856705f7317a8b4e92a2074dc460b5f34e19f8eb5bfd080826c3a61a41414668d22d0352e4b300d1ce09c659243ef8c06492fc22
7
+ data.tar.gz: 33b8d47d5290acea68f1f07ee74a6129ad0a2568fff2ddc783b7910f2ed55ae2d6d6b6f0b1b90b8dea773bfab0c9cc7ccc70723ab5aee75db3e3a0f997b93fe9
@@ -16,7 +16,7 @@ jobs:
16
16
  - 2.7.0-bionic
17
17
  task:
18
18
  - test
19
- - smoke
19
+ - test:output
20
20
  - build
21
21
  container:
22
22
  image: rubylang/ruby:${{ matrix.container_tag }}
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.40.0 (2021-01-31)
6
+
7
+ * Report progress with dots ([#287](https://github.com/soutaro/steep/pull/287))
8
+ * Diagnostics message improvements ([#297](https://github.com/soutaro/steep/pull/297), [#301](https://github.com/soutaro/steep/pull/301))
9
+ * Fix error on implicit `to_proc` syntax when `untyped` is yielded ([#291](https://github.com/soutaro/steep/pull/291))
10
+
5
11
  ## 0.39.0 (2020-12-25)
6
12
 
7
13
  * Update RBS to 1.0.0 ([#282](https://github.com/soutaro/steep/pull/282))
data/Rakefile CHANGED
@@ -9,6 +9,9 @@ end
9
9
 
10
10
  task :default => :test
11
11
 
12
- task :smoke do
13
- sh "bundle", "exec", "bin/smoke_runner.rb", *Dir.glob("smoke/*")
12
+ namespace :test do
13
+ desc "Run output test"
14
+ task :output do
15
+ sh "ruby", "bin/output_test.rb"
16
+ end
14
17
  end
@@ -0,0 +1,49 @@
1
+ require "pathname"
2
+ require "yaml"
3
+ require "open3"
4
+ require "tempfile"
5
+
6
+ if ARGV.empty?
7
+ test_dirs = (Pathname(__dir__) + "../smoke").children
8
+ else
9
+ test_dirs = ARGV.map {|p| Pathname.pwd + p }
10
+ end
11
+
12
+ test_dirs.each do |dir|
13
+ test = dir + "test.yaml"
14
+
15
+ if test.file?
16
+ content = YAML.load_file(test)
17
+ else
18
+ content = { "test" => {} }
19
+ end
20
+
21
+ puts "Rebaselining #{dir}..."
22
+
23
+ command = content["command"] || "steep check"
24
+ puts " command: #{command}"
25
+
26
+ output, _ = Open3.capture2(command, chdir: dir.to_s)
27
+
28
+ diagnostics = output.split(/\n\n/).each.with_object({}) do |message, hash|
29
+ if message =~ /\A([^:]+):\d+:\d+:/
30
+ path = $1
31
+ hash[path] ||= { "diagnostics" => [] }
32
+ hash[path]["diagnostics"] << message.chomp + "\n"
33
+ end
34
+ end
35
+
36
+ content["test"].each_key do |path|
37
+ unless diagnostics.key?(path)
38
+ diagnostics[path] = { "diagnostics" => [] }
39
+ end
40
+ end
41
+
42
+ content["test"] = diagnostics.keys.sort.each.with_object({}) do |key, hash|
43
+ hash[key] = diagnostics[key]
44
+ end
45
+
46
+ test.open("w") do |io|
47
+ YAML.dump(content, io, header: false)
48
+ end
49
+ end
@@ -0,0 +1,93 @@
1
+ require "pathname"
2
+ require "yaml"
3
+ require "open3"
4
+ require "tempfile"
5
+ require "optparse"
6
+
7
+ OptionParser.new do |opts|
8
+ opts.on("--verbose", "-v") { @verbose = true }
9
+ end.parse!(ARGV)
10
+
11
+ if ARGV.empty?
12
+ test_dirs = (Pathname(__dir__) + "../smoke").children
13
+ else
14
+ test_dirs = ARGV.map {|p| Pathname.pwd + p }
15
+ end
16
+
17
+ success = true
18
+
19
+ test_dirs.each do |dir|
20
+ test = dir + "test.yaml"
21
+
22
+ next unless test.file?
23
+
24
+ puts "Running test #{dir}..."
25
+
26
+ content = YAML.load_file(test)
27
+
28
+ command = content["command"] || "steep check"
29
+ puts " command: #{command}"
30
+
31
+ output, _ = Open3.capture2(command, chdir: dir.to_s)
32
+
33
+ if @verbose
34
+ puts " Raw output:"
35
+ output.split(/\n/).each do |line|
36
+ puts " > #{line.chomp}"
37
+ end
38
+ end
39
+
40
+ diagnostics = output.split(/\n\n/).each.with_object({}) do |d, hash|
41
+ if d =~ /\A([^:]+):\d+:\d+:/
42
+ path = $1
43
+ hash[path] ||= []
44
+ hash[path] << (d.chomp + "\n")
45
+ end
46
+ end
47
+
48
+ content["test"].each do |path, test|
49
+ puts " Checking: #{path}..."
50
+
51
+ fail_expected = test["fail"] || false
52
+
53
+ expected_diagnostics = test["diagnostics"]
54
+ reported_diagnostics = (diagnostics[path] || [])
55
+
56
+ puts " # of expected: #{expected_diagnostics.size}, # of reported: #{reported_diagnostics.size}"
57
+
58
+ unexpected_diagnostics = reported_diagnostics.reject {|d| expected_diagnostics.include?(d) }
59
+ missing_diagnostics = expected_diagnostics.reject {|d| reported_diagnostics.include?(d) }
60
+
61
+ unexpected_diagnostics.each do |d|
62
+ puts " Unexpected diagnostics:"
63
+ d.split(/\n/).each do |line|
64
+ puts " + #{line.chomp}"
65
+ end
66
+ end
67
+
68
+ missing_diagnostics.each do |d|
69
+ puts " Missing diagnostics:"
70
+ d.split(/\n/).each do |line|
71
+ puts " - #{line.chomp}"
72
+ end
73
+ end
74
+
75
+ if unexpected_diagnostics.empty? && missing_diagnostics.empty?
76
+ puts " 👍"
77
+ else
78
+ if fail_expected
79
+ puts " 🚨 (expected failure)"
80
+ else
81
+ puts " 🚨"
82
+ success = false
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ if success
89
+ puts "All tests ok! 👏"
90
+ else
91
+ puts "Errors detected! 🤮"
92
+ exit 1
93
+ end
@@ -42,6 +42,8 @@ require "steep/ast/annotation/collection"
42
42
  require "steep/ast/builtin"
43
43
  require "steep/ast/types/factory"
44
44
 
45
+ require "steep/range_extension"
46
+
45
47
  require "steep/interface/function"
46
48
  require "steep/interface/block"
47
49
  require "steep/interface/method_type"
@@ -56,12 +58,14 @@ require "steep/subtyping/constraints"
56
58
  require "steep/subtyping/variable_variance"
57
59
  require "steep/subtyping/variable_occurrence"
58
60
 
59
- require "steep/signature/errors"
61
+ require "steep/diagnostic/helper"
62
+ require "steep/diagnostic/ruby"
63
+ require "steep/diagnostic/signature"
64
+ require "steep/diagnostic/lsp_formatter"
60
65
  require "steep/signature/validator"
61
66
  require "steep/source"
62
67
  require "steep/annotation_parser"
63
68
  require "steep/typing"
64
- require "steep/errors"
65
69
  require "steep/module_helper"
66
70
  require "steep/type_construction"
67
71
  require "steep/type_inference/context"
@@ -97,6 +101,7 @@ require "steep/project/dsl"
97
101
  require "steep/project/file_loader"
98
102
  require "steep/project/hover_content"
99
103
  require "steep/project/completion_provider"
104
+ require "steep/project/stats_calculator"
100
105
  require "steep/drivers/utils/driver_helper"
101
106
  require "steep/drivers/check"
102
107
  require "steep/drivers/stats"
@@ -104,12 +109,12 @@ require "steep/drivers/validate"
104
109
  require "steep/drivers/annotations"
105
110
  require "steep/drivers/watch"
106
111
  require "steep/drivers/langserver"
107
- require "steep/drivers/signature_error_printer"
108
112
  require "steep/drivers/trace_printer"
109
113
  require "steep/drivers/print_project"
110
114
  require "steep/drivers/init"
111
115
  require "steep/drivers/vendor"
112
116
  require "steep/drivers/worker"
117
+ require "steep/drivers/diagnostic_printer"
113
118
 
114
119
  if ENV["NO_COLOR"]
115
120
  Rainbow.enabled = false
@@ -83,7 +83,6 @@ module Steep
83
83
  opts.banner = "Usage: steep check [options] [sources]"
84
84
 
85
85
  opts.on("--steepfile=PATH") {|path| check.steepfile = Pathname(path) }
86
- opts.on("--dump-all-types") { check.dump_all_types = true }
87
86
  handle_logging_options opts
88
87
  end.parse!(argv)
89
88
 
@@ -184,6 +183,7 @@ module Steep
184
183
  opts.on("--signature") { command.worker_type = :signature }
185
184
  opts.on("--steepfile=PATH") {|path| command.steepfile = Pathname(path) }
186
185
  opts.on("--name=NAME") {|name| command.worker_name = name }
186
+ opts.on("--delay-shutdown") { command.delay_shutdown = true }
187
187
  end.parse!(argv)
188
188
  end.run
189
189
  end
@@ -0,0 +1,17 @@
1
+ module Steep
2
+ module Diagnostic
3
+ module Helper
4
+ def error_name
5
+ self.class.name.split(/::/).last
6
+ end
7
+
8
+ def full_message
9
+ if detail = detail_lines
10
+ "#{header_line}\n#{detail}"
11
+ else
12
+ "#{header_line}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Steep
2
+ module Diagnostic
3
+ class LSPFormatter
4
+ LSP = LanguageServer::Protocol
5
+
6
+ def format(diagnostic)
7
+ LSP::Interface::Diagnostic.new(
8
+ message: diagnostic.full_message,
9
+ code: diagnostic.diagnostic_code,
10
+ severity: LSP::Constant::DiagnosticSeverity::ERROR,
11
+ range: diagnostic.location.as_lsp_range
12
+ ).to_hash
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,623 @@
1
+ module Steep
2
+ module Diagnostic
3
+ module Ruby
4
+ class Base
5
+ include Helper
6
+
7
+ attr_reader :node
8
+ attr_reader :location
9
+
10
+ def initialize(node:, location: node.location.expression)
11
+ @node = node
12
+ @location = location
13
+ end
14
+
15
+ def header_line
16
+ error_name
17
+ end
18
+
19
+ def detail_lines
20
+ nil
21
+ end
22
+
23
+ def diagnostic_code
24
+ "Ruby::#{error_name}"
25
+ end
26
+ end
27
+
28
+ module ResultPrinter
29
+ def print_result_to(io, level: 1)
30
+ printer = Drivers::TracePrinter.new(io)
31
+ printer.print result.trace, level: level
32
+ end
33
+
34
+ def trace_lines
35
+ StringIO.new.tap do |io|
36
+ print_result_to(io)
37
+ end.string.chomp
38
+ end
39
+
40
+ def detail_lines
41
+ StringIO.new.tap do |io|
42
+ print_result_to(io)
43
+ end.string.chomp
44
+ end
45
+ end
46
+
47
+ class IncompatibleAssignment < Base
48
+ attr_reader :lhs_type
49
+ attr_reader :rhs_type
50
+ attr_reader :result
51
+
52
+ include ResultPrinter
53
+
54
+ def initialize(node:, lhs_type:, rhs_type:, result:)
55
+ super(node: node)
56
+ @lhs_type = lhs_type
57
+ @rhs_type = rhs_type
58
+ @result = result
59
+ end
60
+
61
+ def header_line
62
+ element = case node.type
63
+ when :ivasgn, :lvasgn, :gvasgn, :cvasgn
64
+ "a variable"
65
+ when :casgn
66
+ "a constant"
67
+ else
68
+ "an expression"
69
+ end
70
+ "Cannot assign a value of type `#{rhs_type}` to #{element} of type `#{lhs_type}`"
71
+ end
72
+ end
73
+
74
+ class IncompatibleArguments < Base
75
+ attr_reader :node
76
+ attr_reader :method_name
77
+ attr_reader :receiver_type
78
+ attr_reader :method_types
79
+
80
+ def initialize(node:, method_name:, receiver_type:, method_types:)
81
+ location = case node.type
82
+ when :send
83
+ node.loc.selector
84
+ when :block
85
+ node.children[0].yield_self do |node|
86
+ node.loc.selector
87
+ end
88
+ when :super
89
+ node.loc.expression
90
+ else
91
+ Steep.logger.error { "Unexpected node given: #{node.type} (IncompatibleArguments#initialize)"}
92
+ node.loc.expression
93
+ end
94
+ super(node: node, location: location)
95
+ @receiver_type = receiver_type
96
+ @method_types = method_types
97
+ @method_name = method_name
98
+ end
99
+
100
+ def header_line
101
+ "Cannot find method `#{method_name}` of type `#{receiver_type}` with compatible arity"
102
+ end
103
+
104
+ def detail_lines
105
+ StringIO.new.tap do |io|
106
+ io.puts "Method types:"
107
+ first_type, *rest_types = method_types
108
+ defn = " def #{method_name}"
109
+ io.puts "#{defn}: #{first_type}"
110
+ rest_types.each do |method_type|
111
+ io.puts "#{" " * defn.size}| #{method_type}"
112
+ end
113
+ end.string.chomp
114
+ end
115
+ end
116
+
117
+ class UnresolvedOverloading < Base
118
+ attr_reader :node
119
+ attr_reader :receiver_type
120
+ attr_reader :method_name
121
+ attr_reader :method_types
122
+
123
+ def initialize(node:, receiver_type:, method_name:, method_types:)
124
+ super node: node
125
+ @receiver_type = receiver_type
126
+ @method_name = method_name
127
+ @method_types = method_types
128
+ end
129
+
130
+ def header_line
131
+ "Cannot find compatible overloading of method `#{method_name}` of type `#{receiver_type}`"
132
+ end
133
+
134
+ def detail_lines
135
+ StringIO.new.tap do |io|
136
+ io.puts "Method types:"
137
+ first_type, *rest_types = method_types
138
+ defn = " def #{method_name}"
139
+ io.puts "#{defn}: #{first_type}"
140
+ rest_types.each do |method_type|
141
+ io.puts "#{" " * defn.size}| #{method_type}"
142
+ end
143
+ end.string.chomp
144
+ end
145
+ end
146
+
147
+ class ArgumentTypeMismatch < Base
148
+ attr_reader :node
149
+ attr_reader :expected
150
+ attr_reader :actual
151
+ attr_reader :receiver_type
152
+ attr_reader :result
153
+
154
+ include ResultPrinter
155
+
156
+ def initialize(node:, receiver_type:, expected:, actual:, result:)
157
+ super(node: node)
158
+ @receiver_type = receiver_type
159
+ @expected = expected
160
+ @actual = actual
161
+ @result = result
162
+ end
163
+
164
+ def header_line
165
+ "Cannot pass a value of type `#{actual}` as an argument of type `#{expected}`"
166
+ end
167
+ end
168
+
169
+ class NoMethod < Base
170
+ attr_reader :type
171
+ attr_reader :method
172
+
173
+ def initialize(node:, type:, method:)
174
+ loc = case node.type
175
+ when :send
176
+ node.loc.selector
177
+ when :block
178
+ node.children[0].loc.selector
179
+ else
180
+ node.loc.expression
181
+ end
182
+ super(node: node, location: loc)
183
+ @type = type
184
+ @method = method
185
+ end
186
+
187
+ def header_line
188
+ "Type `#{type}` does not have method `#{method}`"
189
+ end
190
+ end
191
+
192
+ class ReturnTypeMismatch < Base
193
+ attr_reader :expected
194
+ attr_reader :actual
195
+ attr_reader :result
196
+
197
+ include ResultPrinter
198
+
199
+ def initialize(node:, expected:, actual:, result:)
200
+ super(node: node)
201
+ @expected = expected
202
+ @actual = actual
203
+ @result = result
204
+ end
205
+
206
+ def header_line
207
+ "The method cannot return a value of type `#{actual}` because declared as type `#{expected}`"
208
+ end
209
+ end
210
+
211
+ class UnexpectedBlockGiven < Base
212
+ attr_reader :method_type
213
+
214
+ def initialize(node:, method_type:)
215
+ loc = node.loc.begin.join(node.loc.end)
216
+ super(node: node, location: loc)
217
+ @method_type = method_type
218
+ end
219
+
220
+ def header_line
221
+ "The method cannot be called with a block"
222
+ end
223
+
224
+ def to_s
225
+ format_message "method_type=#{method_type}"
226
+ end
227
+ end
228
+
229
+ class RequiredBlockMissing < Base
230
+ attr_reader :method_type
231
+
232
+ def initialize(node:, method_type:)
233
+ super(node: node, location: node.loc.selector)
234
+ @method_type = method_type
235
+ end
236
+
237
+ def header_line
238
+ "The method cannot be called without a block"
239
+ end
240
+ end
241
+
242
+ class BlockTypeMismatch < Base
243
+ attr_reader :expected
244
+ attr_reader :actual
245
+ attr_reader :result
246
+
247
+ include ResultPrinter
248
+
249
+ def initialize(node:, expected:, actual:, result:)
250
+ super(node: node)
251
+ @expected = expected
252
+ @actual = actual
253
+ @result = result
254
+ end
255
+
256
+ def header_line
257
+ "Cannot pass a value of type `#{actual}` as a block-pass-argument of type `#{expected}`"
258
+ end
259
+ end
260
+
261
+ class BlockBodyTypeMismatch < Base
262
+ attr_reader :expected
263
+ attr_reader :actual
264
+ attr_reader :result
265
+
266
+ include ResultPrinter
267
+
268
+ def initialize(node:, expected:, actual:, result:)
269
+ super(node: node, location: node.loc.begin.join(node.loc.end))
270
+ @expected = expected
271
+ @actual = actual
272
+ @result = result
273
+ end
274
+
275
+ def header_line
276
+ "Cannot allow block body have type `#{actual}` because declared as type `#{expected}`"
277
+ end
278
+ end
279
+
280
+ class BreakTypeMismatch < Base
281
+ attr_reader :expected
282
+ attr_reader :actual
283
+ attr_reader :result
284
+
285
+ include ResultPrinter
286
+
287
+ def initialize(node:, expected:, actual:, result:)
288
+ super(node: node)
289
+ @expected = expected
290
+ @actual = actual
291
+ @result = result
292
+ end
293
+
294
+ def header_line
295
+ "Cannot break with a value of type `#{actual}` because type `#{expected}` is assumed"
296
+ end
297
+ end
298
+
299
+ class UnexpectedJump < Base
300
+ def header_line
301
+ "Cannot jump from here"
302
+ end
303
+ end
304
+
305
+ class UnexpectedJumpValue < Base
306
+ def header_line
307
+ "The value given to #{node.type} will be ignored"
308
+ end
309
+ end
310
+
311
+ class MethodArityMismatch < Base
312
+ attr_reader :method_type
313
+
314
+ def initialize(node:, method_type:)
315
+ args = case node.type
316
+ when :def
317
+ node.children[1]
318
+ when :defs
319
+ node.children[2]
320
+ end
321
+ super(node: node, location: args&.loc&.expression || node.loc.name)
322
+ @method_type = method_type
323
+ end
324
+
325
+ def header_line
326
+ "Method parameters are incompatible with declaration `#{method_type}`"
327
+ end
328
+ end
329
+
330
+ class IncompatibleMethodTypeAnnotation < Base
331
+ attr_reader :interface_method
332
+ attr_reader :annotation_method
333
+ attr_reader :result
334
+
335
+ include ResultPrinter
336
+
337
+ def initialize(node:, interface_method:, annotation_method:, result:)
338
+ raise
339
+ super(node: node)
340
+ @interface_method = interface_method
341
+ @annotation_method = annotation_method
342
+ @result = result
343
+ end
344
+ end
345
+
346
+ class MethodReturnTypeAnnotationMismatch < Base
347
+ attr_reader :method_type
348
+ attr_reader :annotation_type
349
+ attr_reader :result
350
+
351
+ include ResultPrinter
352
+
353
+ def initialize(node:, method_type:, annotation_type:, result:)
354
+ super(node: node)
355
+ @method_type = method_type
356
+ @annotation_type = annotation_type
357
+ @result = result
358
+ end
359
+
360
+ def header_line
361
+ "Annotation `@type return` specifies type `#{annotation_type}` where declared as type `#{method_type}`"
362
+ end
363
+ end
364
+
365
+ class MethodBodyTypeMismatch < Base
366
+ attr_reader :expected
367
+ attr_reader :actual
368
+ attr_reader :result
369
+
370
+ include ResultPrinter
371
+
372
+ def initialize(node:, expected:, actual:, result:)
373
+ super(node: node)
374
+ @expected = expected
375
+ @actual = actual
376
+ @result = result
377
+ end
378
+
379
+ def header_line
380
+ "Cannot allow method body have type `#{actual}` because declared as type `#{expected}`"
381
+ end
382
+ end
383
+
384
+ class UnexpectedYield < Base
385
+ def header_line
386
+ "No block given for `yield`"
387
+ end
388
+ end
389
+
390
+ class UnexpectedSuper < Base
391
+ attr_reader :method
392
+
393
+ def initialize(node:, method:)
394
+ super(node: node)
395
+ @method = method
396
+ end
397
+
398
+ def to_s
399
+ format_message "method=#{method}"
400
+ end
401
+ end
402
+
403
+ class MethodDefinitionMissing < Base
404
+ attr_reader :module_name
405
+ attr_reader :kind
406
+ attr_reader :missing_method
407
+
408
+ def initialize(node:, module_name:, kind:, missing_method:)
409
+ super(node: node, location: node.children[0].loc.expression)
410
+ @module_name = module_name
411
+ @kind = kind
412
+ @missing_method = missing_method
413
+ end
414
+
415
+ def header_line
416
+ method_name = case kind
417
+ when :module
418
+ ".#{missing_method}"
419
+ when :instance
420
+ "##{missing_method}"
421
+ end
422
+ "Cannot find implementation of method `#{module_name}#{method_name}`"
423
+ end
424
+ end
425
+
426
+ class UnexpectedDynamicMethod < Base
427
+ attr_reader :module_name
428
+ attr_reader :method_name
429
+
430
+ def initialize(node:, module_name:, method_name:)
431
+ super(node: node, location: node.children[0].loc.expression)
432
+ @module_name = module_name
433
+ @method_name = method_name
434
+ end
435
+
436
+ def header_line
437
+ "@dynamic annotation contains unknown method name `#{method_name}`"
438
+ end
439
+ end
440
+
441
+ class UnknownConstantAssigned < Base
442
+ attr_reader :context
443
+ attr_reader :name
444
+
445
+ def initialize(node:, context:, name:)
446
+ const = node.children[0]
447
+ loc = if const
448
+ const.loc.expression.join(node.loc.name)
449
+ else
450
+ node.loc.name
451
+ end
452
+ super(node: node, location: loc)
453
+ @context = context
454
+ @name = name
455
+ end
456
+
457
+ def header_line
458
+ "Cannot find the declaration of constant `#{name}`"
459
+ end
460
+ end
461
+
462
+ class FallbackAny < Base
463
+ def initialize(node:)
464
+ super(node: node)
465
+ end
466
+
467
+ def header_line
468
+ "Cannot detect the type of the expression"
469
+ end
470
+ end
471
+
472
+ class UnsatisfiableConstraint < Base
473
+ attr_reader :method_type
474
+ attr_reader :var
475
+ attr_reader :sub_type
476
+ attr_reader :super_type
477
+ attr_reader :result
478
+
479
+ def initialize(node:, method_type:, var:, sub_type:, super_type:, result:)
480
+ super(node: node)
481
+ @method_type = method_type
482
+ @var = var
483
+ @sub_type = sub_type
484
+ @super_type = super_type
485
+ @result = result
486
+ end
487
+
488
+ include ResultPrinter
489
+
490
+ def header_line
491
+ "Unsatisfiable constraint `#{sub_type} <: #{var} <: #{super_type}` is generated through #{method_type}"
492
+ end
493
+ end
494
+
495
+ class IncompatibleAnnotation < Base
496
+ attr_reader :var_name
497
+ attr_reader :result
498
+ attr_reader :relation
499
+
500
+ def initialize(node:, var_name:, result:, relation:)
501
+ super(node: node)
502
+ @var_name = var_name
503
+ @result = result
504
+ @relation = relation
505
+ end
506
+
507
+ include ResultPrinter
508
+
509
+ def header_line
510
+ "Type annotation about `#{var_name}` is incompatible since #{relation} doesn't hold"
511
+ end
512
+ end
513
+
514
+ class IncompatibleTypeCase < Base
515
+ attr_reader :var_name
516
+ attr_reader :result
517
+ attr_reader :relation
518
+
519
+ def initialize(node:, var_name:, result:, relation:)
520
+ super(node: node)
521
+ @var_name = var_name
522
+ @result = result
523
+ @relation = relation
524
+ end
525
+
526
+ include ResultPrinter
527
+
528
+ def header_line
529
+ "Type annotation for branch about `#{var_name}` is incompatible since #{relation} doesn't hold"
530
+ end
531
+ end
532
+
533
+ class ElseOnExhaustiveCase < Base
534
+ attr_reader :type
535
+
536
+ def initialize(node:, type:)
537
+ super(node: node)
538
+ @type = type
539
+ end
540
+
541
+ def header_line
542
+ "The branch is unreachable because the condition is exhaustive"
543
+ end
544
+ end
545
+
546
+ class UnexpectedSplat < Base
547
+ attr_reader :type
548
+
549
+ def initialize(node:, type:)
550
+ super(node: node)
551
+ @type = type
552
+ end
553
+
554
+ def header_line
555
+ "Hash splat is given with object other than `Hash[X, Y]`"
556
+ end
557
+ end
558
+
559
+ class UnexpectedKeyword < Base
560
+ attr_reader :unexpected_keywords
561
+
562
+ def initialize(node:, unexpected_keywords:)
563
+ super(node: node)
564
+ @unexpected_keywords = unexpected_keywords
565
+ end
566
+
567
+ def header_line
568
+ keywords = unexpected_keywords.sort.map {|x| "`#{x}`" }
569
+ "Cannot specify unexpected keyword arguments: #{keywords.join(", ")}"
570
+ end
571
+ end
572
+
573
+ class MissingKeyword < Base
574
+ attr_reader :missing_keywords
575
+
576
+ def initialize(node:, missing_keywords:)
577
+ super(node: node)
578
+ @missing_keywords = missing_keywords
579
+ end
580
+
581
+ def header_line
582
+ keywords = missing_keywords.sort.map {|x| "`#{x}`" }
583
+ "Cannot omit required keywords: #{keywords.join(", ")}"
584
+ end
585
+ end
586
+
587
+ class UnsupportedSyntax < Base
588
+ attr_reader :message
589
+
590
+ def initialize(node:, message: nil)
591
+ super(node: node)
592
+ @message = message
593
+ end
594
+
595
+ def header_line
596
+ if message
597
+ message
598
+ else
599
+ "Syntax `#{node.type}` is not supported in Steep"
600
+ end
601
+ end
602
+ end
603
+
604
+ class UnexpectedError < Base
605
+ attr_reader :message
606
+ attr_reader :error
607
+
608
+ def initialize(node:, error:)
609
+ super(node: node)
610
+ @error = error
611
+ end
612
+
613
+ def header_line
614
+ "UnexpectedError: #{error.message}"
615
+ end
616
+
617
+ def detail_lines
618
+ error.backtrace.join("\n")
619
+ end
620
+ end
621
+ end
622
+ end
623
+ end