steep 0.39.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
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