steep 1.6.0 → 1.7.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) 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/bin/output_test.rb +1 -0
  9. data/doc/narrowing.md +195 -0
  10. data/gemfile_steep/Gemfile.lock +30 -15
  11. data/guides/src/getting-started/getting-started.md +10 -11
  12. data/lib/steep/ast/ignore.rb +148 -0
  13. data/lib/steep/cli.rb +6 -1
  14. data/lib/steep/diagnostic/ruby.rb +16 -0
  15. data/lib/steep/drivers/utils/driver_helper.rb +22 -11
  16. data/lib/steep/node_helper.rb +12 -0
  17. data/lib/steep/project/dsl.rb +18 -21
  18. data/lib/steep/project/options.rb +39 -2
  19. data/lib/steep/project.rb +11 -7
  20. data/lib/steep/server/change_buffer.rb +2 -2
  21. data/lib/steep/server/interaction_worker.rb +61 -11
  22. data/lib/steep/server/worker_process.rb +3 -1
  23. data/lib/steep/services/completion_provider.rb +39 -8
  24. data/lib/steep/services/file_loader.rb +3 -2
  25. data/lib/steep/services/signature_help_provider.rb +9 -8
  26. data/lib/steep/services/type_check_service.rb +36 -8
  27. data/lib/steep/source/ignore_ranges.rb +69 -0
  28. data/lib/steep/source.rb +10 -4
  29. data/lib/steep/type_construction.rb +6 -147
  30. data/lib/steep/type_inference/case_when.rb +301 -0
  31. data/lib/steep/version.rb +1 -1
  32. data/lib/steep.rb +14 -1
  33. data/rbs_collection.steep.lock.yaml +8 -6
  34. data/sig/shims/parser.rbs +10 -0
  35. data/sig/shims/yaml.rbs +4 -0
  36. data/sig/steep/ast/ignore.rbs +66 -0
  37. data/sig/steep/diagnostic/ruby.rbs +8 -0
  38. data/sig/steep/node_helper.rbs +2 -0
  39. data/sig/steep/project/dsl.rbs +8 -4
  40. data/sig/steep/project/options.rbs +21 -1
  41. data/sig/steep/project.rbs +3 -3
  42. data/sig/steep/server/change_buffer.rbs +6 -4
  43. data/sig/steep/server/interaction_worker.rbs +10 -0
  44. data/sig/steep/server/worker_process.rbs +4 -4
  45. data/sig/steep/services/completion_provider.rbs +14 -1
  46. data/sig/steep/services/type_check_service.rbs +6 -1
  47. data/sig/steep/source/ignore_ranges.rbs +38 -0
  48. data/sig/steep/source.rbs +5 -2
  49. data/sig/steep/subtyping/check.rbs +2 -2
  50. data/sig/steep/type_construction.rbs +1 -26
  51. data/sig/steep/type_inference/branch.rbs +15 -0
  52. data/sig/steep/type_inference/case_when.rbs +130 -0
  53. data/sig/steep.rbs +9 -0
  54. data/steep.gemspec +1 -1
  55. metadata +12 -258
  56. data/smoke/alias/Steepfile +0 -6
  57. data/smoke/alias/a.rb +0 -16
  58. data/smoke/alias/a.rbs +0 -10
  59. data/smoke/alias/b.rb +0 -6
  60. data/smoke/alias/c.rb +0 -8
  61. data/smoke/alias/test_expectations.yml +0 -96
  62. data/smoke/and/Steepfile +0 -6
  63. data/smoke/and/a.rb +0 -8
  64. data/smoke/and/test_expectations.yml +0 -29
  65. data/smoke/array/Steepfile +0 -6
  66. data/smoke/array/a.rb +0 -18
  67. data/smoke/array/b.rb +0 -12
  68. data/smoke/array/c.rb +0 -6
  69. data/smoke/array/test_expectations.yml +0 -103
  70. data/smoke/block/Steepfile +0 -6
  71. data/smoke/block/a.rb +0 -10
  72. data/smoke/block/a.rbs +0 -6
  73. data/smoke/block/b.rb +0 -13
  74. data/smoke/block/c.rb +0 -9
  75. data/smoke/block/c.rbs +0 -3
  76. data/smoke/block/d.rb +0 -11
  77. data/smoke/block/e.rb +0 -12
  78. data/smoke/block/e.rbs +0 -4
  79. data/smoke/block/test_expectations.yml +0 -133
  80. data/smoke/case/Steepfile +0 -6
  81. data/smoke/case/a.rb +0 -18
  82. data/smoke/case/test_expectations.yml +0 -47
  83. data/smoke/class/Steepfile +0 -6
  84. data/smoke/class/a.rb +0 -25
  85. data/smoke/class/a.rbs +0 -23
  86. data/smoke/class/b.rb +0 -5
  87. data/smoke/class/c.rb +0 -9
  88. data/smoke/class/f.rb +0 -10
  89. data/smoke/class/g.rb +0 -6
  90. data/smoke/class/h.rb +0 -19
  91. data/smoke/class/h.rbs +0 -6
  92. data/smoke/class/i.rb +0 -14
  93. data/smoke/class/i.rbs +0 -9
  94. data/smoke/class/test_expectations.yml +0 -117
  95. data/smoke/compact/Steepfile +0 -6
  96. data/smoke/compact/a.rb +0 -2
  97. data/smoke/compact/a.rbs +0 -5
  98. data/smoke/compact/b.rb +0 -2
  99. data/smoke/compact/test_expectations.yml +0 -18
  100. data/smoke/const/Steepfile +0 -6
  101. data/smoke/const/a.rb +0 -27
  102. data/smoke/const/b.rb +0 -7
  103. data/smoke/const/b.rbs +0 -5
  104. data/smoke/const/test_expectations.yml +0 -134
  105. data/smoke/diagnostics/Steepfile +0 -6
  106. data/smoke/diagnostics/a.rbs +0 -22
  107. data/smoke/diagnostics/argument_type_mismatch.rb +0 -1
  108. data/smoke/diagnostics/block_body_type_mismatch.rb +0 -1
  109. data/smoke/diagnostics/block_type_mismatch.rb +0 -3
  110. data/smoke/diagnostics/break_type_mismatch.rb +0 -1
  111. data/smoke/diagnostics/different_method_parameter_kind.rb +0 -9
  112. data/smoke/diagnostics/else_on_exhaustive_case.rb +0 -12
  113. data/smoke/diagnostics/incompatible_annotation.rb +0 -6
  114. data/smoke/diagnostics/incompatible_argument.rb +0 -1
  115. data/smoke/diagnostics/incompatible_assignment.rb +0 -8
  116. data/smoke/diagnostics/method_arity_mismatch.rb +0 -11
  117. data/smoke/diagnostics/method_body_type_mismatch.rb +0 -6
  118. data/smoke/diagnostics/method_definition_missing.rb +0 -2
  119. data/smoke/diagnostics/method_parameter_mismatch.rb +0 -10
  120. data/smoke/diagnostics/method_return_type_annotation_mismatch.rb +0 -7
  121. data/smoke/diagnostics/missing_keyword.rb +0 -1
  122. data/smoke/diagnostics/no_method.rb +0 -1
  123. data/smoke/diagnostics/proc_type_expected.rb +0 -3
  124. data/smoke/diagnostics/required_block_missing.rb +0 -1
  125. data/smoke/diagnostics/return_type_mismatch.rb +0 -6
  126. data/smoke/diagnostics/test_expectations.yml +0 -591
  127. data/smoke/diagnostics/unexpected_block_given.rb +0 -1
  128. data/smoke/diagnostics/unexpected_dynamic_method.rb +0 -3
  129. data/smoke/diagnostics/unexpected_jump.rb +0 -4
  130. data/smoke/diagnostics/unexpected_jump_value.rb +0 -3
  131. data/smoke/diagnostics/unexpected_keyword.rb +0 -1
  132. data/smoke/diagnostics/unexpected_splat.rb +0 -1
  133. data/smoke/diagnostics/unexpected_yield.rb +0 -6
  134. data/smoke/diagnostics/unknown_constant_assigned.rb +0 -7
  135. data/smoke/diagnostics/unresolved_overloading.rb +0 -1
  136. data/smoke/diagnostics/unsupported_syntax.rb +0 -2
  137. data/smoke/diagnostics-rbs/Steepfile +0 -8
  138. data/smoke/diagnostics-rbs/duplicated-method-definition.rbs +0 -20
  139. data/smoke/diagnostics-rbs/generic-parameter-mismatch.rbs +0 -7
  140. data/smoke/diagnostics-rbs/inherit-module.rbs +0 -2
  141. data/smoke/diagnostics-rbs/invalid-method-overload.rbs +0 -3
  142. data/smoke/diagnostics-rbs/invalid-type-application.rbs +0 -7
  143. data/smoke/diagnostics-rbs/invalid_variance_annotation.rbs +0 -3
  144. data/smoke/diagnostics-rbs/mixin-class-error.rbs +0 -6
  145. data/smoke/diagnostics-rbs/nonregular-type-alias.rbs +0 -3
  146. data/smoke/diagnostics-rbs/recursive-alias.rbs +0 -5
  147. data/smoke/diagnostics-rbs/recursive-class.rbs +0 -8
  148. data/smoke/diagnostics-rbs/recursive-type-alias.rbs +0 -3
  149. data/smoke/diagnostics-rbs/superclass-mismatch.rbs +0 -7
  150. data/smoke/diagnostics-rbs/test_expectations.yml +0 -300
  151. data/smoke/diagnostics-rbs/unknown-method-alias.rbs +0 -3
  152. data/smoke/diagnostics-rbs/unknown-type-name-2.rbs +0 -5
  153. data/smoke/diagnostics-rbs/unknown-type-name.rbs +0 -13
  154. data/smoke/diagnostics-rbs-duplicated/Steepfile +0 -6
  155. data/smoke/diagnostics-rbs-duplicated/a.rbs +0 -5
  156. data/smoke/diagnostics-rbs-duplicated/test_expectations.yml +0 -13
  157. data/smoke/diagnostics-ruby-unsat/Steepfile +0 -6
  158. data/smoke/diagnostics-ruby-unsat/a.rbs +0 -3
  159. data/smoke/diagnostics-ruby-unsat/test_expectations.yml +0 -27
  160. data/smoke/diagnostics-ruby-unsat/unsatisfiable_constraint.rb +0 -6
  161. data/smoke/dstr/Steepfile +0 -6
  162. data/smoke/dstr/a.rb +0 -5
  163. data/smoke/dstr/test_expectations.yml +0 -13
  164. data/smoke/ensure/Steepfile +0 -6
  165. data/smoke/ensure/a.rb +0 -18
  166. data/smoke/ensure/test_expectations.yml +0 -62
  167. data/smoke/enumerator/Steepfile +0 -6
  168. data/smoke/enumerator/a.rb +0 -6
  169. data/smoke/enumerator/b.rb +0 -17
  170. data/smoke/enumerator/test_expectations.yml +0 -47
  171. data/smoke/extension/Steepfile +0 -6
  172. data/smoke/extension/a.rb +0 -10
  173. data/smoke/extension/a.rbs +0 -13
  174. data/smoke/extension/b.rb +0 -10
  175. data/smoke/extension/c.rb +0 -9
  176. data/smoke/extension/d.rb +0 -2
  177. data/smoke/extension/e.rb +0 -2
  178. data/smoke/extension/e.rbs +0 -7
  179. data/smoke/extension/f.rb +0 -2
  180. data/smoke/extension/f.rbs +0 -3
  181. data/smoke/extension/test_expectations.yml +0 -73
  182. data/smoke/hash/Steepfile +0 -6
  183. data/smoke/hash/a.rb +0 -17
  184. data/smoke/hash/a.rbs +0 -8
  185. data/smoke/hash/b.rb +0 -6
  186. data/smoke/hash/c.rb +0 -15
  187. data/smoke/hash/d.rb +0 -5
  188. data/smoke/hash/e.rb +0 -1
  189. data/smoke/hash/e.rbs +0 -3
  190. data/smoke/hash/f.rb +0 -11
  191. data/smoke/hash/test_expectations.yml +0 -81
  192. data/smoke/hello/Steepfile +0 -6
  193. data/smoke/hello/hello.rb +0 -11
  194. data/smoke/hello/hello.rbs +0 -7
  195. data/smoke/hello/test_expectations.yml +0 -25
  196. data/smoke/if/Steepfile +0 -6
  197. data/smoke/if/a.rb +0 -20
  198. data/smoke/if/test_expectations.yml +0 -34
  199. data/smoke/implements/Steepfile +0 -6
  200. data/smoke/implements/a.rb +0 -12
  201. data/smoke/implements/a.rbs +0 -6
  202. data/smoke/implements/b.rb +0 -13
  203. data/smoke/implements/b.rbs +0 -12
  204. data/smoke/implements/test_expectations.yml +0 -23
  205. data/smoke/initialize/Steepfile +0 -6
  206. data/smoke/initialize/a.rb +0 -12
  207. data/smoke/initialize/a.rbs +0 -3
  208. data/smoke/initialize/test_expectations.yml +0 -1
  209. data/smoke/integer/Steepfile +0 -6
  210. data/smoke/integer/a.rb +0 -26
  211. data/smoke/integer/test_expectations.yml +0 -110
  212. data/smoke/interface/Steepfile +0 -6
  213. data/smoke/interface/a.rb +0 -12
  214. data/smoke/interface/a.rbs +0 -12
  215. data/smoke/interface/test_expectations.yml +0 -23
  216. data/smoke/kwbegin/Steepfile +0 -6
  217. data/smoke/kwbegin/a.rb +0 -7
  218. data/smoke/kwbegin/test_expectations.yml +0 -17
  219. data/smoke/lambda/Steepfile +0 -6
  220. data/smoke/lambda/a.rb +0 -10
  221. data/smoke/lambda/test_expectations.yml +0 -17
  222. data/smoke/literal/Steepfile +0 -6
  223. data/smoke/literal/a.rb +0 -11
  224. data/smoke/literal/b.rb +0 -7
  225. data/smoke/literal/literal_methods.rbs +0 -4
  226. data/smoke/literal/test_expectations.yml +0 -106
  227. data/smoke/map/Steepfile +0 -6
  228. data/smoke/map/a.rb +0 -5
  229. data/smoke/map/test_expectations.yml +0 -1
  230. data/smoke/method/Steepfile +0 -6
  231. data/smoke/method/a.rb +0 -21
  232. data/smoke/method/a.rbs +0 -4
  233. data/smoke/method/b.rb +0 -25
  234. data/smoke/method/c.rb +0 -5
  235. data/smoke/method/d.rb +0 -1
  236. data/smoke/method/d.rbs +0 -3
  237. data/smoke/method/test_expectations.yml +0 -121
  238. data/smoke/module/Steepfile +0 -6
  239. data/smoke/module/a.rb +0 -19
  240. data/smoke/module/a.rbs +0 -16
  241. data/smoke/module/b.rb +0 -6
  242. data/smoke/module/c.rb +0 -22
  243. data/smoke/module/d.rb +0 -4
  244. data/smoke/module/e.rb +0 -13
  245. data/smoke/module/f.rb +0 -11
  246. data/smoke/module/test_expectations.yml +0 -75
  247. data/smoke/regexp/Steepfile +0 -6
  248. data/smoke/regexp/a.rb +0 -109
  249. data/smoke/regexp/b.rb +0 -79
  250. data/smoke/regexp/test_expectations.yml +0 -615
  251. data/smoke/regression/Steepfile +0 -6
  252. data/smoke/regression/array.rb +0 -7
  253. data/smoke/regression/block_param_split.rb +0 -7
  254. data/smoke/regression/block_param_split.rbs +0 -3
  255. data/smoke/regression/empty_yield.rb +0 -5
  256. data/smoke/regression/empty_yield.rbs +0 -3
  257. data/smoke/regression/enumerator_product.rb +0 -1
  258. data/smoke/regression/fun.rb +0 -8
  259. data/smoke/regression/fun.rbs +0 -4
  260. data/smoke/regression/hash.rb +0 -7
  261. data/smoke/regression/hello world.rb +0 -1
  262. data/smoke/regression/issue_328.rb +0 -1
  263. data/smoke/regression/issue_328.rbs +0 -0
  264. data/smoke/regression/issue_332.rb +0 -11
  265. data/smoke/regression/issue_332.rbs +0 -19
  266. data/smoke/regression/issue_372.rb +0 -8
  267. data/smoke/regression/issue_372.rbs +0 -4
  268. data/smoke/regression/lambda.rb +0 -3
  269. data/smoke/regression/masgn.rb +0 -4
  270. data/smoke/regression/poly_new.rb +0 -2
  271. data/smoke/regression/poly_new.rbs +0 -4
  272. data/smoke/regression/range.rb +0 -5
  273. data/smoke/regression/set_divide.rb +0 -12
  274. data/smoke/regression/test_expectations.yml +0 -120
  275. data/smoke/regression/thread.rb +0 -7
  276. data/smoke/rescue/Steepfile +0 -6
  277. data/smoke/rescue/a.rb +0 -48
  278. data/smoke/rescue/test_expectations.yml +0 -79
  279. data/smoke/self/Steepfile +0 -6
  280. data/smoke/self/a.rb +0 -21
  281. data/smoke/self/a.rbs +0 -4
  282. data/smoke/self/test_expectations.yml +0 -23
  283. data/smoke/skip/Steepfile +0 -6
  284. data/smoke/skip/skip.rb +0 -13
  285. data/smoke/skip/test_expectations.yml +0 -23
  286. data/smoke/stdout/Steepfile +0 -6
  287. data/smoke/stdout/a.rb +0 -8
  288. data/smoke/stdout/a.rbs +0 -7
  289. data/smoke/stdout/test_expectations.yml +0 -1
  290. data/smoke/super/Steepfile +0 -6
  291. data/smoke/super/a.rb +0 -30
  292. data/smoke/super/a.rbs +0 -10
  293. data/smoke/super/test_expectations.yml +0 -69
  294. data/smoke/toplevel/Steepfile +0 -6
  295. data/smoke/toplevel/a.rb +0 -3
  296. data/smoke/toplevel/a.rbs +0 -3
  297. data/smoke/toplevel/test_expectations.yml +0 -15
  298. data/smoke/tsort/Steepfile +0 -7
  299. data/smoke/tsort/a.rb +0 -12
  300. data/smoke/tsort/test_expectations.yml +0 -1
  301. data/smoke/type_case/Steepfile +0 -6
  302. data/smoke/type_case/a.rb +0 -24
  303. data/smoke/type_case/test_expectations.yml +0 -58
  304. data/smoke/unexpected/Steepfile +0 -6
  305. data/smoke/unexpected/test_expectations.yml +0 -13
  306. data/smoke/unexpected/unexpected.rbs +0 -3
  307. data/smoke/yield/Steepfile +0 -6
  308. data/smoke/yield/a.rb +0 -15
  309. data/smoke/yield/b.rb +0 -6
  310. 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.1"
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,