steep 0.11.1 → 0.12.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 (299) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitmodules +3 -0
  4. data/CHANGELOG.md +5 -0
  5. data/README.md +48 -90
  6. data/Rakefile +10 -6
  7. data/Steepfile +1 -0
  8. data/bin/setup +1 -0
  9. data/bin/smoke_runner.rb +9 -14
  10. data/exe/rbs +3 -0
  11. data/exe/ruby-signature +3 -0
  12. data/exe/steep +1 -0
  13. data/lib/steep.rb +32 -26
  14. data/lib/steep/annotation_parser.rb +167 -0
  15. data/lib/steep/ast/annotation/collection.rb +7 -7
  16. data/lib/steep/ast/types.rb +60 -0
  17. data/lib/steep/ast/types/any.rb +1 -1
  18. data/lib/steep/ast/types/factory.rb +535 -0
  19. data/lib/steep/ast/types/name.rb +3 -3
  20. data/lib/steep/ast/types/var.rb +1 -1
  21. data/lib/steep/cli.rb +56 -240
  22. data/lib/steep/drivers/annotations.rb +36 -19
  23. data/lib/steep/drivers/check.rb +55 -91
  24. data/lib/steep/drivers/init.rb +54 -0
  25. data/lib/steep/drivers/langserver.rb +241 -150
  26. data/lib/steep/drivers/print_project.rb +56 -0
  27. data/lib/steep/drivers/signature_error_printer.rb +25 -0
  28. data/lib/steep/drivers/trace_printer.rb +25 -0
  29. data/lib/steep/drivers/utils/driver_helper.rb +26 -0
  30. data/lib/steep/drivers/validate.rb +18 -38
  31. data/lib/steep/drivers/vendor.rb +46 -0
  32. data/lib/steep/drivers/watch.rb +78 -140
  33. data/lib/steep/errors.rb +22 -13
  34. data/lib/steep/interface/interface.rb +91 -0
  35. data/lib/steep/interface/method.rb +0 -4
  36. data/lib/steep/interface/method_type.rb +362 -2
  37. data/lib/steep/interface/substitution.rb +22 -0
  38. data/lib/steep/project.rb +25 -233
  39. data/lib/steep/project/dsl.rb +132 -0
  40. data/lib/steep/project/file.rb +93 -76
  41. data/lib/steep/project/file_loader.rb +63 -0
  42. data/lib/steep/project/options.rb +7 -0
  43. data/lib/steep/project/target.rb +190 -0
  44. data/lib/steep/signature/errors.rb +25 -77
  45. data/lib/steep/signature/validator.rb +122 -0
  46. data/lib/steep/source.rb +12 -7
  47. data/lib/steep/subtyping/check.rb +357 -633
  48. data/lib/steep/subtyping/constraints.rb +2 -2
  49. data/lib/steep/subtyping/trace.rb +23 -0
  50. data/lib/steep/type_construction.rb +509 -455
  51. data/lib/steep/type_inference/constant_env.rb +16 -24
  52. data/lib/steep/type_inference/type_env.rb +26 -18
  53. data/lib/steep/version.rb +1 -1
  54. data/sample/Steepfile +6 -0
  55. data/sample/lib/conference.rb +12 -0
  56. data/sample/sig/conference.rbs +6 -0
  57. data/smoke/alias/Steepfile +4 -0
  58. data/smoke/alias/a.rb +2 -2
  59. data/smoke/alias/{a.rbi → a.rbs} +1 -1
  60. data/smoke/and/Steepfile +4 -0
  61. data/smoke/array/Steepfile +4 -0
  62. data/smoke/array/a.rb +2 -2
  63. data/smoke/array/b.rb +4 -4
  64. data/smoke/array/c.rb +2 -2
  65. data/smoke/block/Steepfile +5 -0
  66. data/smoke/block/{a.rbi → a.rbs} +1 -1
  67. data/smoke/block/{c.rbi → c.rbs} +0 -0
  68. data/smoke/block/d.rb +6 -6
  69. data/smoke/case/Steepfile +4 -0
  70. data/smoke/case/a.rb +4 -3
  71. data/smoke/class/Steepfile +4 -0
  72. data/smoke/class/a.rb +1 -4
  73. data/smoke/class/a.rbs +24 -0
  74. data/smoke/class/h.rb +6 -2
  75. data/smoke/class/{h.rbi → h.rbs} +1 -2
  76. data/smoke/class/i.rb +1 -2
  77. data/smoke/class/i.rbs +9 -0
  78. data/smoke/const/Steepfile +4 -0
  79. data/smoke/dstr/Steepfile +4 -0
  80. data/smoke/ensure/Steepfile +4 -0
  81. data/smoke/ensure/a.rb +1 -1
  82. data/smoke/enumerator/Steepfile +4 -0
  83. data/smoke/enumerator/a.rb +7 -7
  84. data/smoke/enumerator/b.rb +6 -6
  85. data/smoke/extension/Steepfile +4 -0
  86. data/smoke/extension/{a.rbi → a.rbs} +2 -2
  87. data/smoke/extension/{e.rbi → e.rbs} +2 -2
  88. data/smoke/hash/Steepfile +4 -0
  89. data/smoke/hash/{a.rbi → a.rbs} +0 -0
  90. data/smoke/hash/b.rb +2 -2
  91. data/smoke/hash/c.rb +1 -1
  92. data/smoke/hash/e.rbs +3 -0
  93. data/smoke/hash/f.rb +1 -1
  94. data/smoke/hello/Steepfile +4 -0
  95. data/smoke/hello/hello.rbs +7 -0
  96. data/smoke/if/Steepfile +4 -0
  97. data/smoke/implements/Steepfile +4 -0
  98. data/smoke/implements/a.rbs +6 -0
  99. data/smoke/initialize/Steepfile +4 -0
  100. data/smoke/initialize/a.rbs +3 -0
  101. data/smoke/integer/Steepfile +4 -0
  102. data/smoke/integer/a.rb +5 -3
  103. data/smoke/interface/Steepfile +4 -0
  104. data/smoke/interface/{a.rbi → a.rbs} +0 -0
  105. data/smoke/kwbegin/Steepfile +4 -0
  106. data/smoke/lambda/Steepfile +4 -0
  107. data/smoke/lambda/a.rb +9 -2
  108. data/smoke/literal/Steepfile +4 -0
  109. data/smoke/literal/{literal_methods.rbi → literal_methods.rbs} +0 -0
  110. data/smoke/map/Steepfile +4 -0
  111. data/smoke/map/a.rb +1 -1
  112. data/smoke/method/Steepfile +4 -0
  113. data/smoke/method/{a.rbi → a.rbs} +0 -0
  114. data/smoke/method/b.rb +1 -4
  115. data/smoke/method/d.rb +1 -0
  116. data/smoke/method/d.rbs +3 -0
  117. data/smoke/module/Steepfile +4 -0
  118. data/smoke/module/a.rb +1 -1
  119. data/smoke/module/a.rbs +16 -0
  120. data/smoke/module/c.rb +1 -1
  121. data/smoke/regexp/Steepfile +4 -0
  122. data/smoke/regexp/a.rb +2 -2
  123. data/smoke/regexp/b.rb +16 -16
  124. data/smoke/regression/Steepfile +5 -0
  125. data/smoke/regression/array.rb +2 -2
  126. data/smoke/regression/hash.rb +2 -2
  127. data/smoke/regression/poly_new.rb +2 -0
  128. data/smoke/regression/poly_new.rbs +4 -0
  129. data/smoke/regression/set_divide.rb +2 -2
  130. data/smoke/rescue/Steepfile +4 -0
  131. data/smoke/rescue/a.rb +1 -1
  132. data/smoke/self/Steepfile +4 -0
  133. data/smoke/self/a.rbs +4 -0
  134. data/smoke/skip/Steepfile +4 -0
  135. data/smoke/stdout/Steepfile +4 -0
  136. data/smoke/stdout/{a.rbi → a.rbs} +1 -1
  137. data/smoke/super/Steepfile +4 -0
  138. data/smoke/super/a.rbs +10 -0
  139. data/smoke/type_case/Steepfile +4 -0
  140. data/smoke/type_case/a.rb +1 -1
  141. data/smoke/yield/Steepfile +4 -0
  142. data/smoke/yield/a.rb +2 -2
  143. data/steep.gemspec +14 -7
  144. data/vendor/ruby-signature/.github/workflows/ruby.yml +27 -0
  145. data/vendor/ruby-signature/.gitignore +12 -0
  146. data/vendor/ruby-signature/.rubocop.yml +15 -0
  147. data/vendor/ruby-signature/BSDL +22 -0
  148. data/vendor/ruby-signature/COPYING +56 -0
  149. data/vendor/ruby-signature/Gemfile +6 -0
  150. data/vendor/ruby-signature/README.md +93 -0
  151. data/vendor/ruby-signature/Rakefile +66 -0
  152. data/vendor/ruby-signature/bin/annotate-with-rdoc +156 -0
  153. data/vendor/ruby-signature/bin/console +14 -0
  154. data/vendor/ruby-signature/bin/query-rdoc +103 -0
  155. data/vendor/ruby-signature/bin/setup +10 -0
  156. data/vendor/ruby-signature/bin/sort +88 -0
  157. data/vendor/ruby-signature/bin/test_runner.rb +17 -0
  158. data/vendor/ruby-signature/docs/CONTRIBUTING.md +97 -0
  159. data/vendor/ruby-signature/docs/sigs.md +148 -0
  160. data/vendor/ruby-signature/docs/stdlib.md +152 -0
  161. data/vendor/ruby-signature/docs/syntax.md +528 -0
  162. data/vendor/ruby-signature/exe/rbs +3 -0
  163. data/vendor/ruby-signature/exe/ruby-signature +7 -0
  164. data/vendor/ruby-signature/lib/ruby/signature.rb +64 -0
  165. data/vendor/ruby-signature/lib/ruby/signature/ast/annotation.rb +29 -0
  166. data/vendor/ruby-signature/lib/ruby/signature/ast/comment.rb +29 -0
  167. data/vendor/ruby-signature/lib/ruby/signature/ast/declarations.rb +391 -0
  168. data/vendor/ruby-signature/lib/ruby/signature/ast/members.rb +364 -0
  169. data/vendor/ruby-signature/lib/ruby/signature/buffer.rb +52 -0
  170. data/vendor/ruby-signature/lib/ruby/signature/builtin_names.rb +54 -0
  171. data/vendor/ruby-signature/lib/ruby/signature/cli.rb +534 -0
  172. data/vendor/ruby-signature/lib/ruby/signature/constant.rb +28 -0
  173. data/vendor/ruby-signature/lib/ruby/signature/constant_table.rb +152 -0
  174. data/vendor/ruby-signature/lib/ruby/signature/definition.rb +172 -0
  175. data/vendor/ruby-signature/lib/ruby/signature/definition_builder.rb +921 -0
  176. data/vendor/ruby-signature/lib/ruby/signature/environment.rb +283 -0
  177. data/vendor/ruby-signature/lib/ruby/signature/environment_loader.rb +138 -0
  178. data/vendor/ruby-signature/lib/ruby/signature/environment_walker.rb +126 -0
  179. data/vendor/ruby-signature/lib/ruby/signature/errors.rb +189 -0
  180. data/vendor/ruby-signature/lib/ruby/signature/location.rb +104 -0
  181. data/vendor/ruby-signature/lib/ruby/signature/method_type.rb +125 -0
  182. data/vendor/ruby-signature/lib/ruby/signature/namespace.rb +93 -0
  183. data/vendor/ruby-signature/lib/ruby/signature/parser.y +1343 -0
  184. data/vendor/ruby-signature/lib/ruby/signature/prototype/rb.rb +441 -0
  185. data/vendor/ruby-signature/lib/ruby/signature/prototype/rbi.rb +579 -0
  186. data/vendor/ruby-signature/lib/ruby/signature/prototype/runtime.rb +383 -0
  187. data/vendor/ruby-signature/lib/ruby/signature/substitution.rb +48 -0
  188. data/vendor/ruby-signature/lib/ruby/signature/test.rb +28 -0
  189. data/vendor/ruby-signature/lib/ruby/signature/test/errors.rb +63 -0
  190. data/vendor/ruby-signature/lib/ruby/signature/test/hook.rb +290 -0
  191. data/vendor/ruby-signature/lib/ruby/signature/test/setup.rb +58 -0
  192. data/vendor/ruby-signature/lib/ruby/signature/test/spy.rb +324 -0
  193. data/vendor/ruby-signature/lib/ruby/signature/test/test_helper.rb +185 -0
  194. data/vendor/ruby-signature/lib/ruby/signature/test/type_check.rb +256 -0
  195. data/vendor/ruby-signature/lib/ruby/signature/type_name.rb +72 -0
  196. data/vendor/ruby-signature/lib/ruby/signature/types.rb +932 -0
  197. data/vendor/ruby-signature/lib/ruby/signature/variance_calculator.rb +140 -0
  198. data/vendor/ruby-signature/lib/ruby/signature/vendorer.rb +49 -0
  199. data/vendor/ruby-signature/lib/ruby/signature/version.rb +5 -0
  200. data/vendor/ruby-signature/lib/ruby/signature/writer.rb +271 -0
  201. data/vendor/ruby-signature/ruby-signature.gemspec +45 -0
  202. data/vendor/ruby-signature/stdlib/abbrev/abbrev.rbs +3 -0
  203. data/vendor/ruby-signature/stdlib/base64/base64.rbs +15 -0
  204. data/vendor/ruby-signature/stdlib/builtin/array.rbs +1997 -0
  205. data/vendor/ruby-signature/stdlib/builtin/basic_object.rbs +280 -0
  206. data/vendor/ruby-signature/stdlib/builtin/binding.rbs +177 -0
  207. data/vendor/ruby-signature/stdlib/builtin/builtin.rbs +35 -0
  208. data/vendor/ruby-signature/stdlib/builtin/class.rbs +145 -0
  209. data/vendor/ruby-signature/stdlib/builtin/comparable.rbs +116 -0
  210. data/vendor/ruby-signature/stdlib/builtin/complex.rbs +400 -0
  211. data/vendor/ruby-signature/stdlib/builtin/constants.rbs +37 -0
  212. data/vendor/ruby-signature/stdlib/builtin/data.rbs +5 -0
  213. data/vendor/ruby-signature/stdlib/builtin/deprecated.rbs +2 -0
  214. data/vendor/ruby-signature/stdlib/builtin/dir.rbs +419 -0
  215. data/vendor/ruby-signature/stdlib/builtin/encoding.rbs +606 -0
  216. data/vendor/ruby-signature/stdlib/builtin/enumerable.rbs +404 -0
  217. data/vendor/ruby-signature/stdlib/builtin/enumerator.rbs +260 -0
  218. data/vendor/ruby-signature/stdlib/builtin/errno.rbs +781 -0
  219. data/vendor/ruby-signature/stdlib/builtin/errors.rbs +582 -0
  220. data/vendor/ruby-signature/stdlib/builtin/exception.rbs +193 -0
  221. data/vendor/ruby-signature/stdlib/builtin/false_class.rbs +40 -0
  222. data/vendor/ruby-signature/stdlib/builtin/fiber.rbs +68 -0
  223. data/vendor/ruby-signature/stdlib/builtin/fiber_error.rbs +12 -0
  224. data/vendor/ruby-signature/stdlib/builtin/file.rbs +476 -0
  225. data/vendor/ruby-signature/stdlib/builtin/file_test.rbs +59 -0
  226. data/vendor/ruby-signature/stdlib/builtin/float.rbs +696 -0
  227. data/vendor/ruby-signature/stdlib/builtin/gc.rbs +121 -0
  228. data/vendor/ruby-signature/stdlib/builtin/hash.rbs +1029 -0
  229. data/vendor/ruby-signature/stdlib/builtin/integer.rbs +710 -0
  230. data/vendor/ruby-signature/stdlib/builtin/io.rbs +683 -0
  231. data/vendor/ruby-signature/stdlib/builtin/kernel.rbs +574 -0
  232. data/vendor/ruby-signature/stdlib/builtin/marshal.rbs +135 -0
  233. data/vendor/ruby-signature/stdlib/builtin/match_data.rbs +141 -0
  234. data/vendor/ruby-signature/stdlib/builtin/math.rbs +66 -0
  235. data/vendor/ruby-signature/stdlib/builtin/method.rbs +182 -0
  236. data/vendor/ruby-signature/stdlib/builtin/module.rbs +248 -0
  237. data/vendor/ruby-signature/stdlib/builtin/nil_class.rbs +82 -0
  238. data/vendor/ruby-signature/stdlib/builtin/numeric.rbs +409 -0
  239. data/vendor/ruby-signature/stdlib/builtin/object.rbs +824 -0
  240. data/vendor/ruby-signature/stdlib/builtin/proc.rbs +426 -0
  241. data/vendor/ruby-signature/stdlib/builtin/process.rbs +354 -0
  242. data/vendor/ruby-signature/stdlib/builtin/random.rbs +93 -0
  243. data/vendor/ruby-signature/stdlib/builtin/range.rbs +226 -0
  244. data/vendor/ruby-signature/stdlib/builtin/rational.rbs +424 -0
  245. data/vendor/ruby-signature/stdlib/builtin/rb_config.rbs +10 -0
  246. data/vendor/ruby-signature/stdlib/builtin/regexp.rbs +131 -0
  247. data/vendor/ruby-signature/stdlib/builtin/ruby_vm.rbs +14 -0
  248. data/vendor/ruby-signature/stdlib/builtin/signal.rbs +55 -0
  249. data/vendor/ruby-signature/stdlib/builtin/string.rbs +770 -0
  250. data/vendor/ruby-signature/stdlib/builtin/string_io.rbs +13 -0
  251. data/vendor/ruby-signature/stdlib/builtin/struct.rbs +40 -0
  252. data/vendor/ruby-signature/stdlib/builtin/symbol.rbs +230 -0
  253. data/vendor/ruby-signature/stdlib/builtin/thread.rbs +1112 -0
  254. data/vendor/ruby-signature/stdlib/builtin/thread_group.rbs +23 -0
  255. data/vendor/ruby-signature/stdlib/builtin/time.rbs +739 -0
  256. data/vendor/ruby-signature/stdlib/builtin/trace_point.rbs +91 -0
  257. data/vendor/ruby-signature/stdlib/builtin/true_class.rbs +46 -0
  258. data/vendor/ruby-signature/stdlib/builtin/unbound_method.rbs +159 -0
  259. data/vendor/ruby-signature/stdlib/builtin/warning.rbs +17 -0
  260. data/vendor/ruby-signature/stdlib/erb/erb.rbs +18 -0
  261. data/vendor/ruby-signature/stdlib/find/find.rbs +44 -0
  262. data/vendor/ruby-signature/stdlib/pathname/pathname.rbs +21 -0
  263. data/vendor/ruby-signature/stdlib/prime/integer-extension.rbs +23 -0
  264. data/vendor/ruby-signature/stdlib/prime/prime.rbs +188 -0
  265. data/vendor/ruby-signature/stdlib/securerandom/securerandom.rbs +9 -0
  266. data/vendor/ruby-signature/stdlib/set/set.rbs +77 -0
  267. data/vendor/ruby-signature/stdlib/tmpdir/tmpdir.rbs +53 -0
  268. metadata +244 -54
  269. data/.travis.yml +0 -7
  270. data/lib/steep/ast/signature/alias.rb +0 -19
  271. data/lib/steep/ast/signature/class.rb +0 -33
  272. data/lib/steep/ast/signature/const.rb +0 -17
  273. data/lib/steep/ast/signature/env.rb +0 -138
  274. data/lib/steep/ast/signature/extension.rb +0 -21
  275. data/lib/steep/ast/signature/gvar.rb +0 -17
  276. data/lib/steep/ast/signature/interface.rb +0 -31
  277. data/lib/steep/ast/signature/members.rb +0 -115
  278. data/lib/steep/ast/signature/module.rb +0 -21
  279. data/lib/steep/drivers/print_interface.rb +0 -94
  280. data/lib/steep/drivers/scaffold.rb +0 -321
  281. data/lib/steep/drivers/utils/each_signature.rb +0 -31
  282. data/lib/steep/interface/abstract.rb +0 -68
  283. data/lib/steep/interface/builder.rb +0 -637
  284. data/lib/steep/interface/instantiated.rb +0 -163
  285. data/lib/steep/interface/ivar_chain.rb +0 -26
  286. data/lib/steep/parser.y +0 -1278
  287. data/lib/steep/project/listener.rb +0 -53
  288. data/smoke/class/a.rbi +0 -24
  289. data/smoke/class/d.rb +0 -9
  290. data/smoke/class/e.rb +0 -12
  291. data/smoke/class/i.rbi +0 -9
  292. data/smoke/hash/e.rbi +0 -3
  293. data/smoke/hello/hello.rbi +0 -7
  294. data/smoke/implements/a.rbi +0 -6
  295. data/smoke/initialize/a.rbi +0 -3
  296. data/smoke/module/a.rbi +0 -16
  297. data/smoke/self/a.rbi +0 -4
  298. data/smoke/super/a.rbi +0 -10
  299. data/stdlib/builtin.rbi +0 -787
@@ -1,124 +1,88 @@
1
1
  module Steep
2
2
  module Drivers
3
3
  class Check
4
- attr_reader :source_paths
5
- attr_reader :signature_dirs
6
4
  attr_reader :stdout
7
5
  attr_reader :stderr
6
+ attr_reader :command_line_patterns
8
7
 
9
- attr_accessor :accept_implicit_any
10
8
  attr_accessor :dump_all_types
11
- attr_accessor :fallback_any_is_error
12
- attr_accessor :allow_missing_definitions
13
9
 
14
- attr_reader :labeling
10
+ include Utils::DriverHelper
15
11
 
16
- include Utils::EachSignature
17
-
18
- def initialize(source_paths:, signature_dirs:, stdout:, stderr:)
19
- @source_paths = source_paths
20
- @signature_dirs = signature_dirs
12
+ def initialize(stdout:, stderr:)
21
13
  @stdout = stdout
22
14
  @stderr = stderr
15
+ @command_line_patterns = []
23
16
 
24
- self.accept_implicit_any = false
25
17
  self.dump_all_types = false
26
- self.fallback_any_is_error = false
27
- self.allow_missing_definitions = true
28
- end
29
-
30
- def options
31
- Project::Options.new.tap do |opt|
32
- opt.allow_missing_definitions = allow_missing_definitions
33
- opt.fallback_any_is_error = fallback_any_is_error
34
- end
35
18
  end
36
19
 
37
20
  def run
38
- project = Project.new(Project::SyntaxErrorRaisingListener.new)
39
-
40
- source_paths.each do |path|
41
- each_file_in_path(".rb", path) do |file_path|
42
- file = Project::SourceFile.new(path: file_path, options: options)
43
- file.content = file_path.read
44
- project.source_files[file_path] = file
45
- end
46
- end
47
-
48
- signature_dirs.each do |path|
49
- each_file_in_path(".rbi", path) do |file_path|
50
- file = Project::SignatureFile.new(path: file_path)
51
- file.content = file_path.read
52
- project.signature_files[file_path] = file
21
+ project = load_config()
22
+
23
+ loader = Project::FileLoader.new(project: project)
24
+ loader.load_sources(command_line_patterns)
25
+ loader.load_signatures()
26
+
27
+ type_check(project)
28
+
29
+ if self.dump_all_types
30
+ project.targets.each do |target|
31
+ case (status = target.status)
32
+ when Project::Target::TypeCheckStatus
33
+ target.source_files.each_value do |file|
34
+ case (file_status = file.status)
35
+ when Project::SourceFile::TypeCheckStatus
36
+ output_types(file_status.typing)
37
+ end
38
+ end
39
+ end
53
40
  end
54
41
  end
55
42
 
56
- project.type_check
57
-
58
- case signature = project.signature
59
- when Project::SignatureLoaded
60
- output_type_check_result(project)
61
- project.has_type_error? ? 1 : 0
62
- when Project::SignatureHasError
63
- output_signature_errors(project)
64
- 1
65
- end
66
- end
67
-
68
- def output_type_check_result(project)
69
- # @type var project: Project
70
-
71
- if dump_all_types
72
- project.source_files.each_value do |file|
73
- lines = []
74
-
75
- if typing = file.typing
76
- typing.nodes.each_value do |node|
77
- begin
78
- type = typing.type_of(node: node)
79
- lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, type]
80
- rescue
81
- lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, nil]
43
+ project.targets.each do |target|
44
+ Steep.logger.tagged "target=#{target.name}" do
45
+ case (status = target.status)
46
+ when Project::Target::SignatureSyntaxErrorStatus
47
+ printer = SignatureErrorPrinter.new(stdout: stdout, stderr: stderr)
48
+ printer.print_syntax_errors(status.errors)
49
+ when Project::Target::SignatureValidationErrorStatus
50
+ printer = SignatureErrorPrinter.new(stdout: stdout, stderr: stderr)
51
+ printer.print_semantic_errors(status.errors)
52
+ when Project::Target::TypeCheckStatus
53
+ status.type_check_sources.each do |source_file|
54
+ source_file.errors.each do |error|
55
+ error.print_to stdout
82
56
  end
83
57
  end
84
-
85
- lines.sort {|x,y| y <=> x }.reverse_each do |line|
86
- source = line[3].loc.expression.source
87
- stdout.puts "#{line[0]}:(#{line[2].join(",")}):(#{line[1].join(",")}):\t#{line[3].type}:\t#{line[4]}\t(#{source.split(/\n/).first})"
88
- end
89
58
  end
90
59
  end
91
60
  end
92
61
 
93
- project.source_files.each_value do |file|
94
- file.errors&.each do |error|
95
- error.print_to stdout
96
- end
62
+ if project.targets.all? {|target| target.status.is_a?(Project::Target::TypeCheckStatus) && target.errors.empty? }
63
+ Steep.logger.info "No type error found"
64
+ return 0
97
65
  end
66
+
67
+ 1
98
68
  end
99
69
 
100
- def output_signature_errors(project)
101
- project.signature.errors.each do |error|
102
- case error
103
- when Interface::Instantiated::InvalidMethodOverrideError
104
- stdout.puts "😱 #{error.message}"
105
- error.result.trace.each do |s, t|
106
- case s
107
- when Interface::Method
108
- stdout.puts " #{s.name}(#{s.type_name}) <: #{t.name}(#{t.type_name})"
109
- when Interface::MethodType
110
- stdout.puts " #{s} <: #{t} (#{s.location&.name||"?"}:#{s.location&.start_line||"?"})"
111
- else
112
- stdout.puts " #{s} <: #{t}"
113
- end
114
- end
115
- stdout.puts " 🚨 #{error.result.error.message}"
116
- when Interface::Instantiated::InvalidIvarOverrideError
117
- stdout.puts "😱 #{error.message}"
118
- else
119
- stdout.puts "😱 #{error.inspect}"
70
+ def output_types(typing)
71
+ lines = []
72
+
73
+ typing.nodes.each_value do |node|
74
+ begin
75
+ type = typing.type_of(node: node)
76
+ lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, type]
77
+ rescue
78
+ lines << [node.loc.expression.source_buffer.name, [node.loc.last_line,node.loc.last_column], [node.loc.first_line, node.loc.column], node, nil]
120
79
  end
121
80
  end
81
+
82
+ lines.sort {|x,y| y <=> x }.reverse_each do |line|
83
+ source = line[3].loc.expression.source
84
+ stdout.puts "#{line[0]}:(#{line[2].join(",")}):(#{line[1].join(",")}):\t#{line[3].type}:\t#{line[4]}\t(#{source.split(/\n/).first})"
85
+ end
122
86
  end
123
87
  end
124
88
  end
@@ -0,0 +1,54 @@
1
+ module Steep
2
+ module Drivers
3
+ class Init
4
+ attr_reader :stdout
5
+ attr_reader :stderr
6
+ attr_accessor :force_write
7
+
8
+ include Utils::DriverHelper
9
+
10
+ TEMPLATE = <<~EOF
11
+ # target :lib do
12
+ # signature "sig"
13
+ #
14
+ # check "lib" # Directory name
15
+ # check "Gemfile" # File name
16
+ # check "app/models/**/*.rb" # Glob
17
+ # # ignore "lib/templates/*.rb"
18
+ #
19
+ # # library "pathname", "set" # Standard libraries
20
+ # # library "strong_json" # Gems
21
+ # end
22
+
23
+ # target :spec do
24
+ # signature "sig", "sig-private"
25
+ #
26
+ # check "spec"
27
+ #
28
+ # # library "pathname", "set" # Standard libraries
29
+ # # library "rspec"
30
+ # end
31
+ EOF
32
+
33
+ def initialize(stdout:, stderr:)
34
+ @stdout = stdout
35
+ @stderr = stderr
36
+ @force_write = false
37
+ end
38
+
39
+ def run
40
+ path = steepfile || Pathname("Steepfile")
41
+
42
+ if path.file? && !force_write
43
+ stdout.puts "#{path} already exists, --force to overwrite"
44
+ return 1
45
+ end
46
+
47
+ stdout.puts "Writing #{path}..."
48
+ path.write(TEMPLATE)
49
+
50
+ 0
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,192 +1,283 @@
1
1
  module Steep
2
2
  module Drivers
3
3
  class Langserver
4
- attr_reader :source_dirs
5
- attr_reader :signature_dirs
6
- attr_reader :options
7
- attr_reader :subscribers
8
- attr_reader :open_paths
9
-
10
- include Utils::EachSignature
11
-
12
- def initialize(source_dirs:, signature_dirs:)
13
- @source_dirs = source_dirs
14
- @signature_dirs = signature_dirs
15
- @options = Project::Options.new
16
- @subscribers = {}
17
- @open_paths = Set.new
18
-
19
- subscribe :initialize do |request:, notifier:|
20
- LanguageServer::Protocol::Interface::InitializeResult.new(
21
- capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
22
- text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
23
- open_close: true,
24
- change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL,
25
- ),
26
- hover_provider: true
27
- ),
28
- )
29
- end
4
+ attr_reader :stdout
5
+ attr_reader :stderr
6
+ attr_reader :stdin
7
+ attr_reader :latest_update_version
8
+ attr_reader :write_mutex
9
+ attr_reader :type_check_queue
10
+ attr_reader :type_check_thread
30
11
 
31
- subscribe :shutdown do |request:, notifier:|
32
- Steep.logger.warn "Shutting down the server..."
33
- exit
34
- end
12
+ include Utils::DriverHelper
35
13
 
36
- subscribe :"textDocument/didOpen" do |request:, notifier:|
37
- uri = URI.parse(request[:params][:textDocument][:uri])
38
- open_path uri
39
- text = request[:params][:textDocument][:text]
40
- synchronize_project(uri: uri, text: text, notifier: notifier)
41
- end
14
+ TypeCheckRequest = Struct.new(:version, keyword_init: true)
42
15
 
43
- subscribe :"textDocument/didClose" do |request:, notifier:|
44
- uri = URI.parse(request[:params][:textDocument][:uri])
45
- close_path uri
46
- end
16
+ def initialize(stdout:, stderr:, stdin:)
17
+ @stdout = stdout
18
+ @stderr = stderr
19
+ @stdin = stdin
20
+ @write_mutex = Mutex.new
21
+ @type_check_queue = Queue.new
22
+ end
47
23
 
48
- subscribe :"textDocument/didChange" do |request:, notifier:|
49
- uri = URI.parse(request[:params][:textDocument][:uri])
50
- text = request[:params][:contentChanges][0][:text]
51
- synchronize_project(uri: uri, text: text, notifier: notifier)
52
- end
24
+ def writer
25
+ @writer ||= LanguageServer::Protocol::Transport::Io::Writer.new(stdout)
26
+ end
53
27
 
54
- subscribe :"textDocument/hover" do |request:, notifier:|
55
- Steep.logger.warn request.inspect
56
- uri = URI.parse(request[:params][:textDocument][:uri])
57
- line = request[:params][:position][:line]
58
- column = request[:params][:position][:character]
59
- respond_to_hover(uri: uri, line: line, column: column, notifier: notifier, id: request[:id])
60
- end
28
+ def reader
29
+ @reader ||= LanguageServer::Protocol::Transport::Io::Reader.new(stdin)
30
+ end
31
+
32
+ def project
33
+ @project or raise "Empty #project"
61
34
  end
62
35
 
63
- def respond_to_hover(uri:, line:, column:, notifier:, id:)
64
- path = Pathname(uri.path).relative_path_from(Pathname.pwd)
36
+ def enqueue_type_check(version)
37
+ @latest_update_version = version
38
+ type_check_queue << TypeCheckRequest.new(version: version)
39
+ end
65
40
 
66
- if path.extname == ".rb"
67
- # line in LSP is zero-origin
68
- project.type_of(path: path, line: line + 1, column: column) do |type, node|
69
- Steep.logger.warn "type = #{type.to_s}"
41
+ def run
42
+ @project = load_config()
70
43
 
71
- start_position = { line: node.location.line - 1, character: node.location.column }
72
- end_position = { line: node.location.last_line - 1, character: node.location.last_column }
73
- range = { start: start_position, end: end_position }
44
+ loader = Project::FileLoader.new(project: project)
45
+ loader.load_sources([])
46
+ loader.load_signatures()
74
47
 
75
- Steep.logger.warn "node = #{node.type}"
76
- Steep.logger.warn "range = #{range.inspect}"
48
+ start_type_check()
77
49
 
78
- LanguageServer::Protocol::Interface::Hover.new(
79
- contents: { kind: "markdown", value: "`#{type}`" },
80
- range: range
81
- )
50
+ reader.read do |request|
51
+ Steep.logger.tagged "lsp" do
52
+ Steep.logger.debug { "Received a request: request=#{request.to_json}" }
53
+ handle_request(request) do |id, result|
54
+ if id
55
+ write_mutex.synchronize do
56
+ Steep.logger.debug { "Writing response to #{id}: #{result.to_json}" }
57
+ writer.write(id: id, result: result)
58
+ end
59
+ end
60
+ end
82
61
  end
83
62
  end
63
+
64
+ 0
84
65
  end
85
66
 
86
- def subscribe(method, &callback)
87
- @subscribers[method] = callback
67
+ def write(method:, params:)
68
+ write_mutex.synchronize do
69
+ Steep.logger.debug { "Sending request: method=#{method}, params=#{params.to_json}"}
70
+ writer.write(method: method, params: params)
71
+ end
88
72
  end
89
73
 
90
- def project
91
- @project ||= Project.new.tap do |project|
92
- source_dirs.each do |path|
93
- each_file_in_path(".rb", path) do |file_path|
94
- file = Project::SourceFile.new(path: file_path, options: options)
95
- file.content = file_path.read
96
- project.source_files[file_path] = file
74
+ def handle_request(request)
75
+ id = request[:id]
76
+ method = request[:method].to_sym
77
+
78
+ Steep.logger.tagged "id=#{id}, method=#{method}" do
79
+ case method
80
+ when :initialize
81
+ yield id, LanguageServer::Protocol::Interface::InitializeResult.new(
82
+ capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
83
+ text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
84
+ change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::FULL
85
+ ),
86
+ hover_provider: true,
87
+ )
88
+ )
89
+
90
+ enqueue_type_check nil
91
+ when :"textDocument/didChange"
92
+ uri = URI.parse(request[:params][:textDocument][:uri])
93
+ path = project.relative_path(Pathname(uri.path))
94
+ text = request[:params][:contentChanges][0][:text]
95
+
96
+ Steep.logger.debug { "path=#{path}, content=#{text.lines.first&.chomp}..." }
97
+
98
+ project.targets.each do |target|
99
+ Steep.logger.tagged "target=#{target.name}" do
100
+ case
101
+ when target.source_file?(path)
102
+ if text.empty? && !path.file?
103
+ Steep.logger.info { "Deleting source file: #{path}..." }
104
+ target.remove_source(path)
105
+ report_diagnostics path, []
106
+ else
107
+ Steep.logger.info { "Updating source file: #{path}..." }
108
+ target.update_source(path, text)
109
+ end
110
+ when target.possible_source_file?(path)
111
+ Steep.logger.info { "Adding source file: #{path}..." }
112
+ target.add_source(path, text)
113
+ when target.signature_file?(path)
114
+ if text.empty? && !path.file?
115
+ Steep.logger.info { "Deleting signature file: #{path}..." }
116
+ target.remove_signature(path)
117
+ report_diagnostics path, []
118
+ else
119
+ Steep.logger.info { "Updating signature file: #{path}..." }
120
+ target.update_signature(path, text)
121
+ end
122
+ when target.possible_signature_file?(path)
123
+ Steep.logger.info { "Adding signature file: #{path}..." }
124
+ target.add_signature(path, text)
125
+ end
126
+ end
97
127
  end
128
+
129
+ version = request[:params][:textDocument][:version]
130
+ enqueue_type_check version
131
+ when :"textDocument/hover"
132
+ uri = URI.parse(request[:params][:textDocument][:uri])
133
+ path = project.relative_path(Pathname(uri.path))
134
+ line = request[:params][:position][:line]
135
+ column = request[:params][:position][:character]
136
+
137
+ yield id, response_to_hover(path: path, line: line, column: column)
138
+
139
+ when :shutdown
140
+ yield id, nil
141
+
142
+ when :exit
143
+ type_check_queue << nil
144
+ type_check_thread.join
145
+ exit
98
146
  end
147
+ end
148
+ end
99
149
 
100
- signature_dirs.each do |path|
101
- each_file_in_path(".rbi", path) do |file_path|
102
- file = Project::SignatureFile.new(path: file_path)
103
- file.content = file_path.read
104
- project.signature_files[file_path] = file
150
+ def start_type_check
151
+ @type_check_thread = Thread.start do
152
+ while request = type_check_queue.deq
153
+ if @latest_update_version == nil || @latest_update_version == request.version
154
+ run_type_check()
105
155
  end
106
156
  end
107
157
  end
108
158
  end
109
159
 
110
- def open_path?(path)
111
- open_paths.member?(path)
160
+ def run_type_check()
161
+ Steep.logger.tagged "#run_type_check" do
162
+ Steep.logger.info { "Running type check..." }
163
+ type_check project
164
+
165
+ Steep.logger.info { "Sending diagnostics..." }
166
+ project.targets.each do |target|
167
+ Steep.logger.tagged "target=#{target.name}, status=#{target.status.class}" do
168
+ Steep.logger.info { "Clearing signature diagnostics..." }
169
+ target.signature_files.each_value do |file|
170
+ report_diagnostics file.path, []
171
+ end
172
+
173
+ case (status = target.status)
174
+ when Project::Target::SignatureValidationErrorStatus
175
+ Steep.logger.info { "Signature validation error" }
176
+ status.errors.group_by(&:path).each do |path, errors|
177
+ diagnostics = errors.map {|error| diagnostic_for_validation_error(error) }
178
+ report_diagnostics path, diagnostics
179
+ end
180
+ when Project::Target::TypeCheckStatus
181
+ Steep.logger.info { "Type check" }
182
+ status.type_check_sources.each do |source|
183
+ diagnostics = case source.status
184
+ when Project::SourceFile::TypeCheckStatus
185
+ source.errors.map {|error| diagnostic_for_type_error(error) }
186
+ when Project::SourceFile::AnnotationSyntaxErrorStatus
187
+ [diagnostics_raw(source.status.error.message, source.status.location)]
188
+ when Project::SourceFile::ParseErrorStatus
189
+ []
190
+ end
191
+
192
+ report_diagnostics source.path, diagnostics
193
+ end
194
+ when Project::Target::SignatureSyntaxErrorStatus
195
+ Steep.logger.info { "Signature syntax error" }
196
+ end
197
+ end
198
+ end
199
+ end
112
200
  end
113
201
 
114
- def open_path(path)
115
- open_paths << path
202
+ def report_diagnostics(path, diagnostics)
203
+ Steep.logger.info { "Reporting #{diagnostics.size} diagnostics for #{path}..." }
204
+ write(
205
+ method: :"textDocument/publishDiagnostics",
206
+ params: LanguageServer::Protocol::Interface::PublishDiagnosticsParams.new(
207
+ uri: URI.parse(project.absolute_path(path).to_s).tap {|uri| uri.scheme = "file"},
208
+ diagnostics: diagnostics,
209
+ )
210
+ )
116
211
  end
117
212
 
118
- def close_path(path)
119
- open_paths.delete path
213
+ def diagnostic_for_validation_error(error)
214
+ LanguageServer::Protocol::Interface::Diagnostic.new(
215
+ message: StringIO.new("").tap {|io| error.puts(io) }.string,
216
+ severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
217
+ range: LanguageServer::Protocol::Interface::Range.new(
218
+ start: LanguageServer::Protocol::Interface::Position.new(
219
+ line: error.location.start_line - 1,
220
+ character: error.location.start_column,
221
+ ),
222
+ end: LanguageServer::Protocol::Interface::Position.new(
223
+ line: error.location.end_line - 1,
224
+ character: error.location.end_column,
225
+ ),
226
+ )
227
+ )
120
228
  end
121
229
 
122
- def run
123
- writer = LanguageServer::Protocol::Transport::Stdio::Writer.new
124
- reader = LanguageServer::Protocol::Transport::Stdio::Reader.new
125
- notifier = Proc.new { |method:, params: {}| writer.write(method: method, params: params) }
230
+ def diagnostics_raw(message, loc)
231
+ LanguageServer::Protocol::Interface::Diagnostic.new(
232
+ message: message,
233
+ severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
234
+ range: LanguageServer::Protocol::Interface::Range.new(
235
+ start: LanguageServer::Protocol::Interface::Position.new(
236
+ line: loc.start_line - 1,
237
+ character: loc.start_column,
238
+ ),
239
+ end: LanguageServer::Protocol::Interface::Position.new(
240
+ line: loc.end_line - 1,
241
+ character: loc.end_column,
242
+ ),
243
+ )
244
+ )
245
+ end
126
246
 
127
- reader.read do |request|
128
- id = request[:id]
129
- method = request[:method].to_sym
130
- Steep.logger.warn "Received event: #{method}"
131
- subscriber = subscribers[method]
132
- if subscriber
133
- result = subscriber.call(request: request, notifier: notifier)
134
- if id && result
135
- writer.write(id: id, result: result)
136
- end
137
- else
138
- Steep.logger.warn "Ignored event: #{method}"
139
- end
140
- end
247
+ def diagnostic_for_type_error(error)
248
+ LanguageServer::Protocol::Interface::Diagnostic.new(
249
+ message: error.to_s,
250
+ severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
251
+ range: LanguageServer::Protocol::Interface::Range.new(
252
+ start: LanguageServer::Protocol::Interface::Position.new(
253
+ line: error.node.loc.line - 1,
254
+ character: error.node.loc.column,
255
+ ),
256
+ end: LanguageServer::Protocol::Interface::Position.new(
257
+ line: error.node.loc.last_line - 1,
258
+ character: error.node.loc.last_column,
259
+ ),
260
+ )
261
+ )
141
262
  end
142
263
 
143
- def synchronize_project(uri:, text:, notifier:)
144
- path = Pathname(uri.path).relative_path_from(Pathname.pwd)
145
-
146
- case path.extname
147
- when ".rb"
148
- file = project.source_files[path] || Project::SourceFile.new(path: path, options: options)
149
- file.content = text
150
- project.source_files[path] = file
151
- when ".rbi"
152
- file = project.signature_files[path] || Project::SignatureFile.new(path: path)
153
- file.content = text
154
- project.signature_files[path] = file
155
- end
264
+ def response_to_hover(path:, line:, column:)
265
+ Steep.logger.info { "path=#{path}, line=#{line}, column=#{column}" }
156
266
 
157
- project.type_check
158
-
159
- open_paths.each do |uri|
160
- Pathname(uri.path).relative_path_from(Pathname.pwd).yield_self do |path|
161
- case path.extname
162
- when ".rb"
163
- file = project.source_files[path] || Project::SourceFile.new(path: path, options: options)
164
- diags = (file.errors || []).map do |error|
165
- LanguageServer::Protocol::Interface::Diagnostic.new(
166
- message: error.to_s,
167
- severity: LanguageServer::Protocol::Constant::DiagnosticSeverity::ERROR,
168
- range: LanguageServer::Protocol::Interface::Range.new(
169
- start: LanguageServer::Protocol::Interface::Position.new(
170
- line: error.node.loc.line - 1,
171
- character: error.node.loc.column,
172
- ),
173
- end: LanguageServer::Protocol::Interface::Position.new(
174
- line: error.node.loc.last_line - 1,
175
- character: error.node.loc.last_column,
176
- ),
177
- )
178
- )
179
- end
267
+ # line in LSP is zero-origin
268
+ project.type_of_node(path: path, line: line + 1, column: column) do |type, node|
269
+ Steep.logger.warn { "node = #{node.type}, type = #{type.to_s}" }
180
270
 
181
- notifier.call(
182
- method: :"textDocument/publishDiagnostics",
183
- params: LanguageServer::Protocol::Interface::PublishDiagnosticsParams.new(
184
- uri: uri,
185
- diagnostics: diags,
186
- ),
187
- )
188
- end
189
- end
271
+ start_position = { line: node.location.line - 1, character: node.location.column }
272
+ end_position = { line: node.location.last_line - 1, character: node.location.last_column }
273
+ range = { start: start_position, end: end_position }
274
+
275
+ Steep.logger.warn { "range = #{range.inspect}" }
276
+
277
+ LanguageServer::Protocol::Interface::Hover.new(
278
+ contents: { kind: "markdown", value: "`#{type}`" },
279
+ range: range
280
+ )
190
281
  end
191
282
  end
192
283
  end