steep 1.6.0 → 1.7.0.dev.2

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 (314) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ruby-windows.yml +2 -1
  4. data/.github/workflows/ruby.yml +9 -10
  5. data/.gitignore +0 -1
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +30 -28
  8. data/Rakefile +5 -0
  9. data/bin/output_test.rb +1 -0
  10. data/doc/narrowing.md +195 -0
  11. data/gemfile_steep/Gemfile +1 -1
  12. data/gemfile_steep/Gemfile.lock +31 -16
  13. data/guides/src/getting-started/getting-started.md +10 -11
  14. data/lib/steep/ast/ignore.rb +148 -0
  15. data/lib/steep/cli.rb +6 -1
  16. data/lib/steep/diagnostic/ruby.rb +16 -0
  17. data/lib/steep/drivers/utils/driver_helper.rb +22 -11
  18. data/lib/steep/node_helper.rb +12 -0
  19. data/lib/steep/project/dsl.rb +18 -21
  20. data/lib/steep/project/options.rb +39 -2
  21. data/lib/steep/project.rb +11 -7
  22. data/lib/steep/server/change_buffer.rb +2 -2
  23. data/lib/steep/server/interaction_worker.rb +61 -11
  24. data/lib/steep/server/worker_process.rb +3 -1
  25. data/lib/steep/services/completion_provider.rb +39 -8
  26. data/lib/steep/services/file_loader.rb +3 -2
  27. data/lib/steep/services/signature_help_provider.rb +20 -17
  28. data/lib/steep/services/type_check_service.rb +36 -8
  29. data/lib/steep/source/ignore_ranges.rb +69 -0
  30. data/lib/steep/source.rb +10 -4
  31. data/lib/steep/subtyping/check.rb +6 -0
  32. data/lib/steep/subtyping/result.rb +6 -0
  33. data/lib/steep/type_construction.rb +6 -151
  34. data/lib/steep/type_inference/case_when.rb +301 -0
  35. data/lib/steep/version.rb +1 -1
  36. data/lib/steep.rb +14 -1
  37. data/rbs_collection.steep.lock.yaml +8 -6
  38. data/sig/shims/parser.rbs +10 -0
  39. data/sig/shims/yaml.rbs +4 -0
  40. data/sig/steep/ast/ignore.rbs +66 -0
  41. data/sig/steep/diagnostic/ruby.rbs +8 -0
  42. data/sig/steep/node_helper.rbs +2 -0
  43. data/sig/steep/project/dsl.rbs +8 -4
  44. data/sig/steep/project/options.rbs +21 -1
  45. data/sig/steep/project.rbs +3 -3
  46. data/sig/steep/server/change_buffer.rbs +6 -4
  47. data/sig/steep/server/interaction_worker.rbs +10 -0
  48. data/sig/steep/server/worker_process.rbs +4 -4
  49. data/sig/steep/services/completion_provider.rbs +14 -1
  50. data/sig/steep/services/type_check_service.rbs +6 -1
  51. data/sig/steep/source/ignore_ranges.rbs +38 -0
  52. data/sig/steep/source.rbs +5 -2
  53. data/sig/steep/subtyping/check.rbs +4 -2
  54. data/sig/steep/subtyping/result.rbs +5 -1
  55. data/sig/steep/type_construction.rbs +1 -26
  56. data/sig/steep/type_inference/case_when.rbs +130 -0
  57. data/sig/steep.rbs +9 -0
  58. data/steep.gemspec +1 -1
  59. metadata +11 -258
  60. data/smoke/alias/Steepfile +0 -6
  61. data/smoke/alias/a.rb +0 -16
  62. data/smoke/alias/a.rbs +0 -10
  63. data/smoke/alias/b.rb +0 -6
  64. data/smoke/alias/c.rb +0 -8
  65. data/smoke/alias/test_expectations.yml +0 -96
  66. data/smoke/and/Steepfile +0 -6
  67. data/smoke/and/a.rb +0 -8
  68. data/smoke/and/test_expectations.yml +0 -29
  69. data/smoke/array/Steepfile +0 -6
  70. data/smoke/array/a.rb +0 -18
  71. data/smoke/array/b.rb +0 -12
  72. data/smoke/array/c.rb +0 -6
  73. data/smoke/array/test_expectations.yml +0 -103
  74. data/smoke/block/Steepfile +0 -6
  75. data/smoke/block/a.rb +0 -10
  76. data/smoke/block/a.rbs +0 -6
  77. data/smoke/block/b.rb +0 -13
  78. data/smoke/block/c.rb +0 -9
  79. data/smoke/block/c.rbs +0 -3
  80. data/smoke/block/d.rb +0 -11
  81. data/smoke/block/e.rb +0 -12
  82. data/smoke/block/e.rbs +0 -4
  83. data/smoke/block/test_expectations.yml +0 -133
  84. data/smoke/case/Steepfile +0 -6
  85. data/smoke/case/a.rb +0 -18
  86. data/smoke/case/test_expectations.yml +0 -47
  87. data/smoke/class/Steepfile +0 -6
  88. data/smoke/class/a.rb +0 -25
  89. data/smoke/class/a.rbs +0 -23
  90. data/smoke/class/b.rb +0 -5
  91. data/smoke/class/c.rb +0 -9
  92. data/smoke/class/f.rb +0 -10
  93. data/smoke/class/g.rb +0 -6
  94. data/smoke/class/h.rb +0 -19
  95. data/smoke/class/h.rbs +0 -6
  96. data/smoke/class/i.rb +0 -14
  97. data/smoke/class/i.rbs +0 -9
  98. data/smoke/class/test_expectations.yml +0 -117
  99. data/smoke/compact/Steepfile +0 -6
  100. data/smoke/compact/a.rb +0 -2
  101. data/smoke/compact/a.rbs +0 -5
  102. data/smoke/compact/b.rb +0 -2
  103. data/smoke/compact/test_expectations.yml +0 -18
  104. data/smoke/const/Steepfile +0 -6
  105. data/smoke/const/a.rb +0 -27
  106. data/smoke/const/b.rb +0 -7
  107. data/smoke/const/b.rbs +0 -5
  108. data/smoke/const/test_expectations.yml +0 -134
  109. data/smoke/diagnostics/Steepfile +0 -6
  110. data/smoke/diagnostics/a.rbs +0 -22
  111. data/smoke/diagnostics/argument_type_mismatch.rb +0 -1
  112. data/smoke/diagnostics/block_body_type_mismatch.rb +0 -1
  113. data/smoke/diagnostics/block_type_mismatch.rb +0 -3
  114. data/smoke/diagnostics/break_type_mismatch.rb +0 -1
  115. data/smoke/diagnostics/different_method_parameter_kind.rb +0 -9
  116. data/smoke/diagnostics/else_on_exhaustive_case.rb +0 -12
  117. data/smoke/diagnostics/incompatible_annotation.rb +0 -6
  118. data/smoke/diagnostics/incompatible_argument.rb +0 -1
  119. data/smoke/diagnostics/incompatible_assignment.rb +0 -8
  120. data/smoke/diagnostics/method_arity_mismatch.rb +0 -11
  121. data/smoke/diagnostics/method_body_type_mismatch.rb +0 -6
  122. data/smoke/diagnostics/method_definition_missing.rb +0 -2
  123. data/smoke/diagnostics/method_parameter_mismatch.rb +0 -10
  124. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +0 -7
  125. data/smoke/diagnostics/missing_keyword.rb +0 -1
  126. data/smoke/diagnostics/no_method.rb +0 -1
  127. data/smoke/diagnostics/proc_type_expected.rb +0 -3
  128. data/smoke/diagnostics/required_block_missing.rb +0 -1
  129. data/smoke/diagnostics/return_type_mismatch.rb +0 -6
  130. data/smoke/diagnostics/test_expectations.yml +0 -591
  131. data/smoke/diagnostics/unexpected_block_given.rb +0 -1
  132. data/smoke/diagnostics/unexpected_dynamic_method.rb +0 -3
  133. data/smoke/diagnostics/unexpected_jump.rb +0 -4
  134. data/smoke/diagnostics/unexpected_jump_value.rb +0 -3
  135. data/smoke/diagnostics/unexpected_keyword.rb +0 -1
  136. data/smoke/diagnostics/unexpected_splat.rb +0 -1
  137. data/smoke/diagnostics/unexpected_yield.rb +0 -6
  138. data/smoke/diagnostics/unknown_constant_assigned.rb +0 -7
  139. data/smoke/diagnostics/unresolved_overloading.rb +0 -1
  140. data/smoke/diagnostics/unsupported_syntax.rb +0 -2
  141. data/smoke/diagnostics-rbs/Steepfile +0 -8
  142. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +0 -20
  143. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +0 -7
  144. data/smoke/diagnostics-rbs/inherit-module.rbs +0 -2
  145. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +0 -3
  146. data/smoke/diagnostics-rbs/invalid-type-application.rbs +0 -7
  147. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +0 -3
  148. data/smoke/diagnostics-rbs/mixin-class-error.rbs +0 -6
  149. data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +0 -3
  150. data/smoke/diagnostics-rbs/recursive-alias.rbs +0 -5
  151. data/smoke/diagnostics-rbs/recursive-class.rbs +0 -8
  152. data/smoke/diagnostics-rbs/recursive-type-alias.rbs +0 -3
  153. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +0 -7
  154. data/smoke/diagnostics-rbs/test_expectations.yml +0 -300
  155. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +0 -3
  156. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +0 -5
  157. data/smoke/diagnostics-rbs/unknown-type-name.rbs +0 -13
  158. data/smoke/diagnostics-rbs-duplicated/Steepfile +0 -6
  159. data/smoke/diagnostics-rbs-duplicated/a.rbs +0 -5
  160. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +0 -13
  161. data/smoke/diagnostics-ruby-unsat/Steepfile +0 -6
  162. data/smoke/diagnostics-ruby-unsat/a.rbs +0 -3
  163. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +0 -27
  164. data/smoke/diagnostics-ruby-unsat/unsatisfiable_constraint.rb +0 -6
  165. data/smoke/dstr/Steepfile +0 -6
  166. data/smoke/dstr/a.rb +0 -5
  167. data/smoke/dstr/test_expectations.yml +0 -13
  168. data/smoke/ensure/Steepfile +0 -6
  169. data/smoke/ensure/a.rb +0 -18
  170. data/smoke/ensure/test_expectations.yml +0 -62
  171. data/smoke/enumerator/Steepfile +0 -6
  172. data/smoke/enumerator/a.rb +0 -6
  173. data/smoke/enumerator/b.rb +0 -17
  174. data/smoke/enumerator/test_expectations.yml +0 -47
  175. data/smoke/extension/Steepfile +0 -6
  176. data/smoke/extension/a.rb +0 -10
  177. data/smoke/extension/a.rbs +0 -13
  178. data/smoke/extension/b.rb +0 -10
  179. data/smoke/extension/c.rb +0 -9
  180. data/smoke/extension/d.rb +0 -2
  181. data/smoke/extension/e.rb +0 -2
  182. data/smoke/extension/e.rbs +0 -7
  183. data/smoke/extension/f.rb +0 -2
  184. data/smoke/extension/f.rbs +0 -3
  185. data/smoke/extension/test_expectations.yml +0 -73
  186. data/smoke/hash/Steepfile +0 -6
  187. data/smoke/hash/a.rb +0 -17
  188. data/smoke/hash/a.rbs +0 -8
  189. data/smoke/hash/b.rb +0 -6
  190. data/smoke/hash/c.rb +0 -15
  191. data/smoke/hash/d.rb +0 -5
  192. data/smoke/hash/e.rb +0 -1
  193. data/smoke/hash/e.rbs +0 -3
  194. data/smoke/hash/f.rb +0 -11
  195. data/smoke/hash/test_expectations.yml +0 -81
  196. data/smoke/hello/Steepfile +0 -6
  197. data/smoke/hello/hello.rb +0 -11
  198. data/smoke/hello/hello.rbs +0 -7
  199. data/smoke/hello/test_expectations.yml +0 -25
  200. data/smoke/if/Steepfile +0 -6
  201. data/smoke/if/a.rb +0 -20
  202. data/smoke/if/test_expectations.yml +0 -34
  203. data/smoke/implements/Steepfile +0 -6
  204. data/smoke/implements/a.rb +0 -12
  205. data/smoke/implements/a.rbs +0 -6
  206. data/smoke/implements/b.rb +0 -13
  207. data/smoke/implements/b.rbs +0 -12
  208. data/smoke/implements/test_expectations.yml +0 -23
  209. data/smoke/initialize/Steepfile +0 -6
  210. data/smoke/initialize/a.rb +0 -12
  211. data/smoke/initialize/a.rbs +0 -3
  212. data/smoke/initialize/test_expectations.yml +0 -1
  213. data/smoke/integer/Steepfile +0 -6
  214. data/smoke/integer/a.rb +0 -26
  215. data/smoke/integer/test_expectations.yml +0 -110
  216. data/smoke/interface/Steepfile +0 -6
  217. data/smoke/interface/a.rb +0 -12
  218. data/smoke/interface/a.rbs +0 -12
  219. data/smoke/interface/test_expectations.yml +0 -23
  220. data/smoke/kwbegin/Steepfile +0 -6
  221. data/smoke/kwbegin/a.rb +0 -7
  222. data/smoke/kwbegin/test_expectations.yml +0 -17
  223. data/smoke/lambda/Steepfile +0 -6
  224. data/smoke/lambda/a.rb +0 -10
  225. data/smoke/lambda/test_expectations.yml +0 -17
  226. data/smoke/literal/Steepfile +0 -6
  227. data/smoke/literal/a.rb +0 -11
  228. data/smoke/literal/b.rb +0 -7
  229. data/smoke/literal/literal_methods.rbs +0 -4
  230. data/smoke/literal/test_expectations.yml +0 -106
  231. data/smoke/map/Steepfile +0 -6
  232. data/smoke/map/a.rb +0 -5
  233. data/smoke/map/test_expectations.yml +0 -1
  234. data/smoke/method/Steepfile +0 -6
  235. data/smoke/method/a.rb +0 -21
  236. data/smoke/method/a.rbs +0 -4
  237. data/smoke/method/b.rb +0 -25
  238. data/smoke/method/c.rb +0 -5
  239. data/smoke/method/d.rb +0 -1
  240. data/smoke/method/d.rbs +0 -3
  241. data/smoke/method/test_expectations.yml +0 -121
  242. data/smoke/module/Steepfile +0 -6
  243. data/smoke/module/a.rb +0 -19
  244. data/smoke/module/a.rbs +0 -16
  245. data/smoke/module/b.rb +0 -6
  246. data/smoke/module/c.rb +0 -22
  247. data/smoke/module/d.rb +0 -4
  248. data/smoke/module/e.rb +0 -13
  249. data/smoke/module/f.rb +0 -11
  250. data/smoke/module/test_expectations.yml +0 -75
  251. data/smoke/regexp/Steepfile +0 -6
  252. data/smoke/regexp/a.rb +0 -109
  253. data/smoke/regexp/b.rb +0 -79
  254. data/smoke/regexp/test_expectations.yml +0 -615
  255. data/smoke/regression/Steepfile +0 -6
  256. data/smoke/regression/array.rb +0 -7
  257. data/smoke/regression/block_param_split.rb +0 -7
  258. data/smoke/regression/block_param_split.rbs +0 -3
  259. data/smoke/regression/empty_yield.rb +0 -5
  260. data/smoke/regression/empty_yield.rbs +0 -3
  261. data/smoke/regression/enumerator_product.rb +0 -1
  262. data/smoke/regression/fun.rb +0 -8
  263. data/smoke/regression/fun.rbs +0 -4
  264. data/smoke/regression/hash.rb +0 -7
  265. data/smoke/regression/hello world.rb +0 -1
  266. data/smoke/regression/issue_328.rb +0 -1
  267. data/smoke/regression/issue_328.rbs +0 -0
  268. data/smoke/regression/issue_332.rb +0 -11
  269. data/smoke/regression/issue_332.rbs +0 -19
  270. data/smoke/regression/issue_372.rb +0 -8
  271. data/smoke/regression/issue_372.rbs +0 -4
  272. data/smoke/regression/lambda.rb +0 -3
  273. data/smoke/regression/masgn.rb +0 -4
  274. data/smoke/regression/poly_new.rb +0 -2
  275. data/smoke/regression/poly_new.rbs +0 -4
  276. data/smoke/regression/range.rb +0 -5
  277. data/smoke/regression/set_divide.rb +0 -12
  278. data/smoke/regression/test_expectations.yml +0 -120
  279. data/smoke/regression/thread.rb +0 -7
  280. data/smoke/rescue/Steepfile +0 -6
  281. data/smoke/rescue/a.rb +0 -48
  282. data/smoke/rescue/test_expectations.yml +0 -79
  283. data/smoke/self/Steepfile +0 -6
  284. data/smoke/self/a.rb +0 -21
  285. data/smoke/self/a.rbs +0 -4
  286. data/smoke/self/test_expectations.yml +0 -23
  287. data/smoke/skip/Steepfile +0 -6
  288. data/smoke/skip/skip.rb +0 -13
  289. data/smoke/skip/test_expectations.yml +0 -23
  290. data/smoke/stdout/Steepfile +0 -6
  291. data/smoke/stdout/a.rb +0 -8
  292. data/smoke/stdout/a.rbs +0 -7
  293. data/smoke/stdout/test_expectations.yml +0 -1
  294. data/smoke/super/Steepfile +0 -6
  295. data/smoke/super/a.rb +0 -30
  296. data/smoke/super/a.rbs +0 -10
  297. data/smoke/super/test_expectations.yml +0 -69
  298. data/smoke/toplevel/Steepfile +0 -6
  299. data/smoke/toplevel/a.rb +0 -3
  300. data/smoke/toplevel/a.rbs +0 -3
  301. data/smoke/toplevel/test_expectations.yml +0 -15
  302. data/smoke/tsort/Steepfile +0 -7
  303. data/smoke/tsort/a.rb +0 -12
  304. data/smoke/tsort/test_expectations.yml +0 -1
  305. data/smoke/type_case/Steepfile +0 -6
  306. data/smoke/type_case/a.rb +0 -24
  307. data/smoke/type_case/test_expectations.yml +0 -58
  308. data/smoke/unexpected/Steepfile +0 -6
  309. data/smoke/unexpected/test_expectations.yml +0 -13
  310. data/smoke/unexpected/unexpected.rbs +0 -3
  311. data/smoke/yield/Steepfile +0 -6
  312. data/smoke/yield/a.rb +0 -15
  313. data/smoke/yield/b.rb +0 -6
  314. data/smoke/yield/test_expectations.yml +0 -88
@@ -0,0 +1,301 @@
1
+ module Steep
2
+ module TypeInference
3
+ class CaseWhen
4
+ class WhenPatterns
5
+ include NodeHelper
6
+
7
+ attr_reader :logic, :initial_constr, :unreachable_clause, :pattern_results
8
+
9
+ def initialize(logic, initial_constr, unreachable_clause, assignment_node)
10
+ @logic = logic
11
+ @initial_constr = initial_constr
12
+ @unreachable_clause = unreachable_clause
13
+ @assignment_node = assignment_node
14
+
15
+ @pattern_results = []
16
+ end
17
+
18
+ def add_pattern(pat)
19
+ test_node = pat.updated(:send, [pat, :===, assignment_node])
20
+
21
+ latest_constr, unreachable_pattern = latest_result
22
+
23
+ type, constr = yield(test_node, latest_constr, unreachable_pattern)
24
+ truthy_result, falsy_result = logic.eval(env: latest_constr.context.type_env, node: test_node)
25
+
26
+ pattern_results << [pat, truthy_result, falsy_result]
27
+ end
28
+
29
+ def latest_result
30
+ if (_, truthy, falsy = pattern_results.last)
31
+ [
32
+ initial_constr.update_type_env { falsy.env },
33
+ falsy.unreachable
34
+ ]
35
+ else
36
+ [initial_constr, unreachable_clause]
37
+ end
38
+ end
39
+
40
+ def body_result
41
+ raise if pattern_results.empty?
42
+
43
+ type_envs = pattern_results.map {|_, truthy, _| truthy.env }
44
+ env = initial_constr.context.type_env.join(*type_envs)
45
+
46
+ env = yield(env) || env
47
+
48
+ [
49
+ initial_constr.update_type_env { env },
50
+ unreachable_clause || pattern_results.all? {|_, truthy, _| truthy.unreachable }
51
+ ]
52
+ end
53
+
54
+ def falsy_result
55
+ (_, _, falsy = pattern_results.last) or raise
56
+
57
+ [
58
+ initial_constr.update_type_env { falsy.env },
59
+ unreachable_clause || falsy.unreachable
60
+ ]
61
+ end
62
+
63
+ def assignment_node()
64
+ clone_node(@assignment_node)
65
+ end
66
+ end
67
+
68
+ include NodeHelper
69
+ extend NodeHelper
70
+
71
+ def self.type_check(constr, node, logic, hint:, condition:)
72
+ case_when = new(node, logic) do |condition_node|
73
+ constr.synthesize(condition_node)
74
+ end
75
+
76
+ case_when.when_clauses() do |when_pats, patterns, body_node, loc|
77
+ patterns.each do |pat|
78
+ when_pats.add_pattern(pat) {|test, constr| constr.synthesize(test) }
79
+ end
80
+
81
+ body_constr, body_unreachable = when_pats.body_result() do |env|
82
+ case_when.propagate_value_node_type(env)
83
+ end
84
+
85
+ if body_node
86
+ body_constr = body_constr.for_branch(body_node)
87
+ type, body_constr = body_constr.synthesize(body_node, hint: hint, condition: condition)
88
+ else
89
+ type = AST::Builtin.nil_type
90
+ end
91
+
92
+ body_result = LogicTypeInterpreter::Result.new(
93
+ type: type,
94
+ env: body_constr.context.type_env,
95
+ unreachable: body_unreachable
96
+ )
97
+
98
+ falsy_constr, falsy_unreachable = when_pats.falsy_result
99
+ next_result = LogicTypeInterpreter::Result.new(
100
+ type: AST::Builtin.any_type, # Unused for falsy pattern
101
+ env: falsy_constr.context.type_env,
102
+ unreachable: falsy_unreachable
103
+ )
104
+
105
+ [body_result, next_result]
106
+ end
107
+
108
+ case_when.else_clause do |else_node, constr|
109
+ constr.synthesize(else_node, hint: hint, condition: condition)
110
+ end
111
+
112
+ case_when.result()
113
+ end
114
+
115
+ attr_reader :location, :node, :condition_node, :when_nodes, :else_node
116
+ attr_reader :initial_constr, :logic, :clause_results, :else_result
117
+ attr_reader :assignment_node, :value_node, :var_name
118
+
119
+ def initialize(node, logic)
120
+ @node = node
121
+
122
+ condition_node, when_nodes, else_node, location = deconstruct_case_node!(node)
123
+ condition_node or raise "CaseWhen works for case-when syntax with condition node"
124
+
125
+ @condition_node = condition_node
126
+ @when_nodes = when_nodes
127
+ @else_node = else_node
128
+ @location = location
129
+ @logic = logic
130
+ @clause_results = []
131
+
132
+ type, constr = yield(condition_node)
133
+
134
+ @var_name = "__case_when:#{SecureRandom.alphanumeric(5)}__".to_sym
135
+ @value_node, @assignment_node = rewrite_condition_node(var_name, condition_node)
136
+
137
+ @initial_constr = constr.update_type_env do |env|
138
+ env.merge(local_variable_types: { var_name => [type, nil] })
139
+ end
140
+ end
141
+
142
+ def when_clauses()
143
+ when_nodes.each do |when_node|
144
+ clause_constr, unreachable = latest_result
145
+
146
+ patterns, body, loc = deconstruct_when_node!(when_node)
147
+
148
+ when_pats = WhenPatterns.new(
149
+ logic,
150
+ clause_constr,
151
+ unreachable,
152
+ assignment_node
153
+ )
154
+
155
+ body_result, next_result = yield(
156
+ when_pats,
157
+ patterns,
158
+ body,
159
+ loc
160
+ )
161
+
162
+ if body_result.unreachable
163
+ if body_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: body_result.type, super_type: AST::Builtin.bottom_type)
164
+ typing.add_error(
165
+ Diagnostic::Ruby::UnreachableValueBranch.new(
166
+ node: when_node,
167
+ type: body_result.type,
168
+ location: loc.keyword
169
+ )
170
+ )
171
+ end
172
+ end
173
+
174
+ clause_results << [body_result, next_result]
175
+ end
176
+ end
177
+
178
+ def else_clause()
179
+ unless else_loc = has_else_clause?
180
+ return
181
+ end
182
+
183
+ constr, unreachable = latest_result
184
+
185
+ constr = constr.update_type_env do |env|
186
+ propagate_value_node_type(env) || env
187
+ end
188
+
189
+ @else_result =
190
+ if else_node
191
+ yield(else_node, constr)
192
+ else
193
+ TypeConstruction::Pair.new(type: AST::Builtin.nil_type, constr: constr)
194
+ end
195
+
196
+ else_result or raise
197
+
198
+ if unreachable
199
+ if else_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: else_result.type, super_type: AST::Builtin.bottom_type)
200
+ typing.add_error(
201
+ Diagnostic::Ruby::UnreachableValueBranch.new(
202
+ node: else_node || node,
203
+ type: else_result.type,
204
+ location: else_loc
205
+ )
206
+ )
207
+ end
208
+ end
209
+ end
210
+
211
+ def latest_result
212
+ if (_, falsy_result = clause_results.last)
213
+ [
214
+ initial_constr.update_type_env { falsy_result.env },
215
+ falsy_result.unreachable
216
+ ]
217
+ else
218
+ [initial_constr, false]
219
+ end
220
+ end
221
+
222
+ def result
223
+ results = clause_results.filter_map do |body, _|
224
+ unless body.unreachable
225
+ body
226
+ end
227
+ end
228
+ next_constr, next_clause_unreachable = latest_result
229
+
230
+ unless next_clause_unreachable
231
+ if else_result
232
+ results << LogicTypeInterpreter::Result.new(
233
+ type: else_result.type,
234
+ env: else_result.context.type_env,
235
+ unreachable: false # Unused
236
+ )
237
+ else
238
+ results << LogicTypeInterpreter::Result.new(
239
+ type: AST::Builtin.nil_type,
240
+ env: next_constr.context.type_env,
241
+ unreachable: false
242
+ )
243
+ end
244
+ end
245
+
246
+ types = results.map {|result| result.type }
247
+ envs = results.map {|result| result.env }
248
+
249
+ [
250
+ types,
251
+ envs
252
+ ]
253
+ end
254
+
255
+ def has_else_clause?
256
+ location.else
257
+ end
258
+
259
+ def typing
260
+ logic.typing
261
+ end
262
+
263
+ def rewrite_condition_node(var_name, node)
264
+ case node.type
265
+ when :lvasgn
266
+ name, rhs = node.children
267
+ value, rhs = rewrite_condition_node(var_name, rhs)
268
+ [value, node.updated(nil, [name, rhs])]
269
+ when :lvar
270
+ name, = node.children
271
+ [
272
+ nil,
273
+ node.updated(:lvasgn, [name, node.updated(:lvar, [var_name])])
274
+ ]
275
+ when :begin
276
+ *children, last = node.children
277
+ value_node, last = rewrite_condition_node(var_name, last)
278
+ [
279
+ value_node,
280
+ node.updated(nil, children.push(last))
281
+ ]
282
+ else
283
+ [
284
+ node,
285
+ node.updated(:lvar, [var_name])
286
+ ]
287
+ end
288
+ end
289
+
290
+ def propagate_value_node_type(env)
291
+ if value_node
292
+ if (call = initial_constr.typing.method_calls[value_node]).is_a?(MethodCall::Typed)
293
+ if env[value_node]
294
+ env.merge(pure_method_calls: { value_node => [call, env[var_name]] })
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
data/lib/steep/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Steep
2
- VERSION = "1.6.0"
2
+ VERSION = "1.7.0.dev.2"
3
3
  end
data/lib/steep.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require "steep/version"
2
2
 
3
3
  require "pathname"
4
- require "parser/ruby32"
4
+ require "parser/ruby33"
5
5
  require "active_support"
6
6
  require "active_support/core_ext/object/try"
7
7
  require "active_support/core_ext/string/inflections"
@@ -52,6 +52,7 @@ require "steep/ast/node/type_assertion"
52
52
  require "steep/ast/node/type_application"
53
53
  require "steep/ast/builtin"
54
54
  require "steep/ast/types/factory"
55
+ require "steep/ast/ignore"
55
56
 
56
57
  require "steep/range_extension"
57
58
 
@@ -76,6 +77,7 @@ require "steep/diagnostic/signature"
76
77
  require "steep/diagnostic/lsp_formatter"
77
78
  require "steep/signature/validator"
78
79
  require "steep/source"
80
+ require "steep/source/ignore_ranges"
79
81
  require "steep/annotation_parser"
80
82
  require "steep/typing"
81
83
  require "steep/module_helper"
@@ -91,6 +93,7 @@ require "steep/type_inference/type_env_builder"
91
93
  require "steep/type_inference/logic_type_interpreter"
92
94
  require "steep/type_inference/multiple_assignment"
93
95
  require "steep/type_inference/method_call"
96
+ require "steep/type_inference/case_when"
94
97
 
95
98
  require "steep/index/rbs_index"
96
99
  require "steep/index/signature_symbol_provider"
@@ -152,6 +155,10 @@ module Steep
152
155
  @logger || raise
153
156
  end
154
157
 
158
+ def self.ui_logger
159
+ @ui_logger || raise
160
+ end
161
+
155
162
  def self.new_logger(output, prev_level)
156
163
  ActiveSupport::TaggedLogging.new(Logger.new(output)).tap do |logger|
157
164
  logger.push_tags "Steep #{VERSION}"
@@ -165,12 +172,18 @@ module Steep
165
172
 
166
173
  def self.log_output=(output)
167
174
  @log_output = output
175
+
168
176
  prev_level = @logger&.level
169
177
  @logger = new_logger(output, prev_level)
178
+
179
+ prev_level = @ui_logger&.level
180
+ @ui_logger = new_logger(output, prev_level)
181
+
170
182
  output
171
183
  end
172
184
 
173
185
  @logger = nil
186
+ @ui_logger = nil
174
187
  self.log_output = STDERR
175
188
 
176
189
  def self.measure(message, level: :warn)
@@ -1,10 +1,4 @@
1
1
  ---
2
- sources:
3
- - type: git
4
- name: ruby/gem_rbs_collection
5
- revision: c42c09528dd99252db98f0744181a6de54ec2f55
6
- remote: https://github.com/ruby/gem_rbs_collection.git
7
- repo_dir: gems
8
2
  path: ".gem_rbs_collection"
9
3
  gems:
10
4
  - name: activesupport
@@ -15,6 +9,14 @@ gems:
15
9
  revision: c42c09528dd99252db98f0744181a6de54ec2f55
16
10
  remote: https://github.com/ruby/gem_rbs_collection.git
17
11
  repo_dir: gems
12
+ - name: base64
13
+ version: '0'
14
+ source:
15
+ type: stdlib
16
+ - name: bigdecimal
17
+ version: '0'
18
+ source:
19
+ type: stdlib
18
20
  - name: concurrent-ruby
19
21
  version: '1.1'
20
22
  source:
data/sig/shims/parser.rbs CHANGED
@@ -35,6 +35,16 @@ module Parser
35
35
  attr_reader diagnostics: untyped
36
36
  end
37
37
 
38
+ class Ruby33
39
+ def initialize: (untyped builder) -> void
40
+
41
+ def parse: (Source::Buffer) -> AST
42
+
43
+ def parse_with_comments: (Source::Buffer) -> [AST::Node, Array[Source::Comment]]
44
+
45
+ attr_reader diagnostics: untyped
46
+ end
47
+
38
48
  module Source
39
49
  class Buffer
40
50
  def initialize: (String file, Integer lineno, source: String) -> void
@@ -0,0 +1,4 @@
1
+ module Psych
2
+ class SyntaxError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,66 @@
1
+ use Parser::Source::Comment, RBS::Buffer, RBS::Location
2
+
3
+ module Steep
4
+ module AST
5
+ module Ignore
6
+ class BufferScanner
7
+ attr_reader scanner: StringScanner
8
+
9
+ attr_reader location: Location[untyped, untyped]
10
+
11
+ def initialize: (Location[untyped, untyped]) -> void
12
+
13
+ def offset: () -> Integer
14
+
15
+ def charpos: () -> Integer
16
+
17
+ def scan: (Regexp) -> Location[bot, bot]?
18
+
19
+ def skip: (Regexp) -> void
20
+
21
+ def eos?: () -> bool
22
+ end
23
+
24
+ type t = IgnoreStart | IgnoreEnd | IgnoreLine
25
+
26
+ def self.parse: (Comment, Buffer) -> t?
27
+
28
+ class IgnoreStart
29
+ attr_reader comment: Comment
30
+
31
+ attr_reader location: Location[bot, bot]
32
+
33
+ def initialize: (Comment, Location[bot, bot]) -> void
34
+
35
+ def line: () -> Integer
36
+ end
37
+
38
+ class IgnoreEnd
39
+ attr_reader comment: Comment
40
+
41
+ attr_reader location: Location[bot, bot]
42
+
43
+ def initialize: (Comment, Location[bot, bot]) -> void
44
+
45
+ def line: () -> Integer
46
+ end
47
+
48
+ class IgnoreLine
49
+ type diagnostic = Location[:name, :following_comma]
50
+ type diagnostics = Array[diagnostic]
51
+
52
+ attr_reader comment: Comment
53
+
54
+ attr_reader location: Location[:keyword, bot]
55
+
56
+ attr_reader raw_diagnostics: diagnostics
57
+
58
+ def initialize: (Comment, diagnostics, Location[:keyword, bot]) -> void
59
+
60
+ def line: () -> Integer
61
+
62
+ def ignored_diagnostics: () -> (:all | Array[String])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -628,6 +628,14 @@ module Steep
628
628
  def initialize: (error: Signature::Base, node: Parser::AST::Node, location: location) -> void
629
629
  end
630
630
 
631
+ # steep:ignore comment is invalid
632
+ #
633
+ class InvalidIgnoreComment < Base
634
+ attr_reader comment: Parser::Source::Comment
635
+
636
+ def initialize: (comment: Parser::Source::Comment) -> void
637
+ end
638
+
631
639
  # Argument forwarding `...` cannot be done safely, because of
632
640
  #
633
641
  # 1. The arguments are incompatible, or
@@ -74,5 +74,7 @@ module Steep
74
74
  # If the next node is a `block` or `numblock` that is associated to the *sendish node*, it is the block node.
75
75
  #
76
76
  def deconstruct_sendish_and_block_nodes: (*Parser::AST::Node) -> [Parser::AST::Node, Parser::AST::Node?]?
77
+
78
+ def clone_node: (Node) -> Node
77
79
  end
78
80
  end
@@ -24,9 +24,13 @@ module Steep
24
24
 
25
25
  attr_reader project: Project?
26
26
 
27
- attr_reader collection_config_path: Pathname?
28
-
29
- NONE: untyped
27
+ # Attribute to keep track of collection configuration
28
+ #
29
+ # * `Pathname` means loading the configuration from the path
30
+ # * `nil` means no configuration is given
31
+ # * `false` means rbs-collection is disabled
32
+ #
33
+ attr_reader collection_config_path: Pathname | nil | false
30
34
 
31
35
  def project!: () -> Project
32
36
 
@@ -86,7 +90,7 @@ module Steep
86
90
  def disable_collection: () -> void
87
91
  end
88
92
 
89
- attr_reader project: untyped
93
+ attr_reader project: Project
90
94
 
91
95
  @@templates: Hash[Symbol, TargetDSL]
92
96
 
@@ -19,9 +19,29 @@ module Steep
19
19
 
20
20
  attr_accessor paths: PathOptions
21
21
 
22
- attr_accessor collection_lock: RBS::Collection::Config::Lockfile?
22
+ attr_accessor collection_config_path: Pathname?
23
23
 
24
24
  def initialize: () -> void
25
+
26
+ # Returns path of lockfile
27
+ %a{pure} def collection_lock_path: () -> Pathname?
28
+
29
+ # Returns `Lockfile` instance if it can be loaded
30
+ #
31
+ %a{pure} def collection_lock: () -> RBS::Collection::Config::Lockfile?
32
+
33
+ @collection_lock: RBS::Collection::Config::Lockfile | Pathname | YAML::SyntaxError | RBS::Collection::Config::CollectionNotAvailable | nil
34
+
35
+ # Load collection configuration
36
+ #
37
+ # * Returns `Lockfile` instance if successfully loaded
38
+ # * Returns `nil` if collection is disabled
39
+ # * Returns `Pathname` if a file is missing
40
+ # * Returns `YAML::SyntaxError` or `CollectionNotAvailable` if an error is raised
41
+ #
42
+ # It keeps the last result unless `force: true` is specified.
43
+ #
44
+ def load_collection_lock: (?force: bool) -> (RBS::Collection::Config::Lockfile | Pathname | YAML::SyntaxError | RBS::Collection::Config::CollectionNotAvailable | nil)
25
45
  end
26
46
  end
27
47
  end
@@ -2,11 +2,11 @@ module Steep
2
2
  class Project
3
3
  attr_reader targets: Array[Target]
4
4
 
5
- attr_reader steepfile_path: Pathname
5
+ attr_reader steepfile_path: Pathname?
6
6
 
7
- def initialize: (steepfile_path: Pathname) -> void
7
+ attr_reader base_dir: Pathname
8
8
 
9
- def base_dir: () -> Pathname
9
+ def initialize: (steepfile_path: Pathname?, ?base_dir: Pathname?) -> void
10
10
 
11
11
  def relative_path: (Pathname path) -> Pathname
12
12
 
@@ -1,13 +1,15 @@
1
1
  module Steep
2
2
  module Server
3
- module ChangeBuffer : _WithProject
3
+ module ChangeBuffer : _WithProject, _WithMutex
4
4
  interface _WithProject
5
5
  def project: () -> Project
6
6
  end
7
7
 
8
- type changes = Hash[Pathname, Array[Services::ContentChange]]
8
+ interface _WithMutex
9
+ def mutex: () -> Mutex
10
+ end
9
11
 
10
- attr_reader mutex: Thread::Mutex
12
+ type changes = Hash[Pathname, Array[Services::ContentChange]]
11
13
 
12
14
  attr_reader buffered_changes: changes
13
15
 
@@ -29,7 +31,7 @@ module Steep
29
31
  def collect_changes: (untyped request) -> void
30
32
 
31
33
  # Reset the content of `uri` to `text`
32
- #
34
+ #
33
35
  def reset_change: (uri: String, text: String) -> void
34
36
  end
35
37
  end
@@ -58,10 +58,20 @@ module Steep
58
58
 
59
59
  attr_reader service: Services::TypeCheckService
60
60
 
61
+ attr_reader mutex: Mutex
62
+
61
63
  def initialize: (project: Project, reader: Reader, writer: Writer, ?queue: Queue) -> void
62
64
 
63
65
  def handle_job: (job) -> void
64
66
 
67
+ @last_job_mutex: Mutex
68
+
69
+ @last_job: job?
70
+
71
+ def process_latest_job: [T] (job) { () -> T } -> T?
72
+
73
+ def queue_job: (job) -> void
74
+
65
75
  type lsp_request = { id: String, method: String, params: untyped }
66
76
 
67
77
  def handle_request: (lsp_request) -> void
@@ -49,7 +49,7 @@ module Steep
49
49
  def self.start_worker: (
50
50
  worker_type `type`,
51
51
  name: String,
52
- steepfile: Pathname,
52
+ steepfile: Pathname?,
53
53
  steep_command: String?,
54
54
  ?patterns: Array[String],
55
55
  ?delay_shutdown: bool,
@@ -59,7 +59,7 @@ module Steep
59
59
  def self.fork_worker: (
60
60
  worker_type `type`,
61
61
  name: String,
62
- steepfile: Pathname,
62
+ steepfile: Pathname?,
63
63
  patterns: Array[String],
64
64
  delay_shutdown: bool,
65
65
  index: [Integer, Integer]?
@@ -68,7 +68,7 @@ module Steep
68
68
  def self.spawn_worker: (
69
69
  worker_type `type`,
70
70
  name: String,
71
- steepfile: Pathname,
71
+ steepfile: Pathname?,
72
72
  steep_command: ::String,
73
73
  patterns: Array[String],
74
74
  delay_shutdown: bool,
@@ -76,7 +76,7 @@ module Steep
76
76
  ) -> WorkerProcess
77
77
 
78
78
  def self.start_typecheck_workers: (
79
- steepfile: Pathname,
79
+ steepfile: Pathname?,
80
80
  args: Array[String],
81
81
  steep_command: ::String?,
82
82
  ?count: Integer,