steep 1.6.0 → 1.7.0.dev.2

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c834be3917dcff04077098f89b0300f7819b0cda30499bd11d34b172627e975f
4
- data.tar.gz: d48ce39f9e337ad408047b56a0bb68a133ba6e044b66381c2bdd6974fcf94a69
3
+ metadata.gz: 0646b6c1cb596e684bc62461ee2dac160e134bc7e126f447df8f6e46a535c8b6
4
+ data.tar.gz: fda1f8d1903253537fec79679dd88f3579404372d4fc611a0d4b9d085e5b3502
5
5
  SHA512:
6
- metadata.gz: 1dca3fc949d3df061bf63f913eda7553c6ecb18a8a8e7f270d81ba118ca1e852326bcee836200c377b2791902e395ac112e79961b4bc614f75e6ff6fca1e5697
7
- data.tar.gz: 30cdc11cfc25432270751e3e8c492424131055062afb5d430a415a4d2000c55b9614db304b36a051e3f42da5e0cc96335296b499a7eff41ce37d6db191e1ce01
6
+ metadata.gz: 46fe48e86a4fe74482765debeb255e1b806298fb797603ebf292443432506e80ac7ffb7aa964b6331e4f71d2f4e607379340834a6f66866d75bc8c0ca4b73633
7
+ data.tar.gz: c822e0bf8a0484914423ece12d3d38bebf4102d6f63b1eb79f779cc3bf8aa9b909d34b189a594cf5d0a4f607ed0474d7b9bb2ed6a6c6d400212e329f4d5a8e96
@@ -7,6 +7,9 @@ updates:
7
7
  interval: daily
8
8
  time: "20:00"
9
9
  open-pull-requests-limit: 10
10
+ allow:
11
+ - dependency-type: all
12
+ versioning-strategy: lockfile-only
10
13
 
11
14
  - package-ecosystem: bundler
12
15
  directory: "/gemfile_steep"
@@ -14,6 +17,9 @@ updates:
14
17
  interval: daily
15
18
  time: "20:00"
16
19
  open-pull-requests-limit: 10
20
+ allow:
21
+ - dependency-type: all
22
+ versioning-strategy: lockfile-only
17
23
 
18
24
  - package-ecosystem: "github-actions"
19
25
  directory: "/"
@@ -7,13 +7,14 @@ on:
7
7
  pull_request: {}
8
8
 
9
9
  jobs:
10
- test:
10
+ windows:
11
11
  strategy:
12
12
  matrix:
13
13
  ruby_version:
14
14
  - "3.0"
15
15
  - "3.1"
16
16
  - "3.2"
17
+ - "3.3"
17
18
  task:
18
19
  - test
19
20
  # - test:output Ignored because the order of diagnostics changes somehow
@@ -9,21 +9,20 @@ on:
9
9
  jobs:
10
10
  test:
11
11
  strategy:
12
+ fail-fast: false
12
13
  matrix:
13
- container_tag:
14
- - "3.0"
15
- - "3.1"
16
- - "3.2"
17
- # - "master-nightly-focal"
14
+ ruby: ['3.0', '3.1', '3.2', '3.3']
18
15
  task:
19
- - test
20
- - test:output
21
- - build
16
+ - test
17
+ - test:output
18
+ - build
22
19
  runs-on: ubuntu-latest
23
- container:
24
- image: rubylang/ruby:${{ matrix.container_tag }}
25
20
  steps:
26
21
  - uses: actions/checkout@v4
22
+ - uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler: none
27
26
  - name: Run test
28
27
  run: |
29
28
  git config --global --add safe.directory /__w/steep/steep
data/.gitignore CHANGED
@@ -2,7 +2,6 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
- /doc/
6
5
  /pkg/
7
6
  /spec/reports/
8
7
  /tmp/
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem "rake"
7
- gem "minitest", "~> 5.20"
7
+ gem "minitest", "~> 5.21"
8
8
  gem "minitest-hooks"
9
9
  group :stackprof, optional: true do
10
10
  gem "stackprof"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (1.6.0)
4
+ steep (1.7.0.dev.2)
5
5
  activesupport (>= 5.1)
6
6
  concurrent-ruby (>= 1.1.10)
7
7
  csv (>= 3.0.9)
@@ -20,7 +20,8 @@ PATH
20
20
  GEM
21
21
  remote: https://rubygems.org/
22
22
  specs:
23
- activesupport (7.1.1)
23
+ abbrev (0.1.2)
24
+ activesupport (7.1.3.2)
24
25
  base64
25
26
  bigdecimal
26
27
  concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -31,40 +32,40 @@ GEM
31
32
  mutex_m
32
33
  tzinfo (~> 2.0)
33
34
  ast (2.4.2)
34
- base64 (0.1.1)
35
- bigdecimal (3.1.4)
36
- concurrent-ruby (1.2.2)
35
+ base64 (0.2.0)
36
+ bigdecimal (3.1.6)
37
+ concurrent-ruby (1.2.3)
37
38
  connection_pool (2.4.1)
38
- csv (3.2.7)
39
- debug (1.8.0)
40
- irb (>= 1.5.0)
41
- reline (>= 0.3.1)
42
- drb (2.1.1)
39
+ csv (3.2.8)
40
+ debug (1.9.1)
41
+ irb (~> 1.10)
42
+ reline (>= 0.3.8)
43
+ drb (2.2.0)
43
44
  ruby2_keywords
44
45
  ffi (1.16.3)
45
- fileutils (1.7.1)
46
+ fileutils (1.7.2)
46
47
  i18n (1.14.1)
47
48
  concurrent-ruby (~> 1.0)
48
- io-console (0.6.0)
49
- irb (1.8.1)
49
+ io-console (0.7.2)
50
+ irb (1.11.1)
50
51
  rdoc
51
- reline (>= 0.3.8)
52
- json (2.6.3)
52
+ reline (>= 0.4.2)
53
+ json (2.7.1)
53
54
  language_server-protocol (3.17.0.3)
54
55
  listen (3.8.0)
55
56
  rb-fsevent (~> 0.10, >= 0.10.3)
56
57
  rb-inotify (~> 0.9, >= 0.9.10)
57
- logger (1.5.3)
58
- minitest (5.20.0)
58
+ logger (1.6.0)
59
+ minitest (5.22.2)
59
60
  minitest-hooks (1.5.1)
60
61
  minitest (> 5.3)
61
62
  minitest-slow_test (0.2.0)
62
63
  minitest (>= 5.0)
63
- mutex_m (0.1.2)
64
- parser (3.2.2.4)
64
+ mutex_m (0.2.0)
65
+ parser (3.3.0.5)
65
66
  ast (~> 2.4.1)
66
67
  racc
67
- psych (5.1.0)
68
+ psych (5.1.2)
68
69
  stringio
69
70
  racc (1.7.3)
70
71
  rainbow (3.1.1)
@@ -72,16 +73,17 @@ GEM
72
73
  rb-fsevent (0.11.2)
73
74
  rb-inotify (0.10.1)
74
75
  ffi (~> 1.0)
75
- rbs (3.2.2)
76
- rdoc (6.5.0)
76
+ rbs (3.4.4)
77
+ abbrev
78
+ rdoc (6.6.2)
77
79
  psych (>= 4.0.0)
78
- reline (0.3.8)
80
+ reline (0.4.2)
79
81
  io-console (~> 0.5)
80
82
  ruby2_keywords (0.0.5)
81
- securerandom (0.2.2)
82
- stackprof (0.2.25)
83
- stringio (3.0.8)
84
- strscan (3.0.7)
83
+ securerandom (0.3.1)
84
+ stackprof (0.2.26)
85
+ stringio (3.1.0)
86
+ strscan (3.1.0)
85
87
  terminal-table (3.0.2)
86
88
  unicode-display_width (>= 1.1.1, < 3)
87
89
  tzinfo (2.0.6)
@@ -93,7 +95,7 @@ PLATFORMS
93
95
 
94
96
  DEPENDENCIES
95
97
  debug
96
- minitest (~> 5.20)
98
+ minitest (~> 5.21)
97
99
  minitest-hooks
98
100
  minitest-slow_test
99
101
  rake
data/Rakefile CHANGED
@@ -188,6 +188,11 @@ NOTES
188
188
  milestone = "Steep #{major}.#{minor}.x"
189
189
  end
190
190
 
191
+ if pre =~ /dev/
192
+ puts "🔍 Skipping `Released` tags because `dev` release: `#{pre}`"
193
+ next
194
+ end
195
+
191
196
  puts "🔍 Finding pull requests that is associated to milestone `#{milestone}`..."
192
197
 
193
198
  command = [
data/bin/output_test.rb CHANGED
@@ -27,6 +27,7 @@ test_dirs.each do |dir|
27
27
  end
28
28
 
29
29
  command = %w(steep check --with-expectations=test_expectations.yml)
30
+ command << "-j2" if ENV["CI"]
30
31
  puts " command: #{command.join(" ")}"
31
32
 
32
33
  output, status = Open3.capture2(*command, chdir: dir.to_s)
data/doc/narrowing.md ADDED
@@ -0,0 +1,195 @@
1
+ # Narrowing Implementation
2
+
3
+ > This is an internal doc for Steep developers. [Narrowing guide](../guides/narrowing/narrowing.md) is for users.
4
+
5
+ The challenge is Ruby has special type predicate methods that should be supported by type checkers. `#nil?` is used instead of `unless` statement to test if a value is a `nil` or not. `#is_a?` or `#===` are used to confirm if an object is an instance of a class. Negation and equality are implemented as methods -- `#!` and `#==`.
6
+
7
+ Steep supports those methods by introducing special types for those methods.
8
+
9
+ ```rbs
10
+ # This is not a valid RBS type definition.
11
+ # Steep implements a transformation from valid RBS syntax to those special types.
12
+ module Kernel
13
+ def nil?: () -> RECEIVER_IS_NIL
14
+
15
+ def is_a?: (Class klass) -> RECEIVER_IS_ARG
16
+ end
17
+ ```
18
+
19
+ When type checking a conditional resulted in `RECEIVER_IS_NIL` type, the type checker overrides the type of the expressions inside the *then* and *else* clauses.
20
+
21
+ ```ruby
22
+ x = [1, ""].sample # The type of `x` is `String | Integer | nil`
23
+
24
+ unless x.is_a?(String) # 1. The condition expression has `RECEIVER_IS_NIL`
25
+ x.upcase # 2. Steep overrides the type of `x` to `String` in *then* clause
26
+ else
27
+ # 3. Steep overrides the type of `x` to `Integer | nil` in *else* clause
28
+ end
29
+ ```
30
+ ## Logical types
31
+
32
+ We extend *type* with *logical types* as follows:
33
+
34
+ ```
35
+ type ::= ...
36
+ | NOT # Negation of type of receiver
37
+ | RECEIVER_IS_NIL # Receiver is nil when it evaluates to truthy
38
+ | RECEIVER_IS_NOT_NIL # Receiver is not nil when it evaluates to truthy
39
+ | RECEIVER_IS_ARG # Receiver is an instance of argument when it evaluates to truthy
40
+ | ARG_IS_RECEIVER # Argument is an instance of receiver when it evaluates to truthy
41
+ | ARG_EQUALS_RECEIVER # Argument is equal to receiver when it evaluates to truthy
42
+ | ENV(original_type, truthy_env, falsy_env) # Two type environments for truthy and falsy
43
+ ```
44
+ ### ENV type
45
+
46
+ `ENV` looks a bit different from others because it takes arguments. The type is used for `and` and `or`.
47
+
48
+ Consider the example with local variables `x` and `y` where both of them have type `String?`.
49
+
50
+ ```ruby
51
+ (x && y) && (x + y) # Parens added for ease of reading
52
+ ```
53
+
54
+ The type of the whole expression is `String?`. When `x` or `y` is `nil`, it evaluates to `nil`. If both of `x` and `y` is a `String`, `x + y` evaluates to `String` because of the definition of `String#+`.
55
+
56
+ The type narrowing starts with the top expression.
57
+
58
+ ```ruby
59
+ (...) && (x + y)
60
+ ```
61
+
62
+ It immediately type checks the left hand side, but with *conditional mode*. Conditional mode is a special flag that the type checking result should keep as many of the environments as possible.
63
+
64
+ ```ruby
65
+ x && y
66
+ ```
67
+
68
+ Going down again, it gets a typing `x: String?` and `y: String?. It runs a type narrowing, to obtain a result both of `x` and `y` are `String` for truthy result, both of `x` and `y` are `String?` for falsy result. The two environments should be propagated to the upper node, because the parent node is also `&&` which is a subject of type narrowing. So, it returns an `ENV` type, `String?` for original type, `{ x: String, y: String }` for truthy result, and `{ x: String?, y: String? }` for falsy result.
69
+
70
+ Going up to the outer `&&` expression. The left hand side has `ENV` type, and then the right hand side is type checked based on the truthy environment (because of the semantics of `&&` expression.) Both `x` and `y` are `String` and it type checks. The type of the whole expression union of `String` and the falsy part of the original type -- `nil`.
71
+ ## Union type partition
72
+
73
+ We introduce *partition* function for union types. It returns a pair of two types -- truthy parts and falsy parts. We need a slightly different variant for *non-nil* partition to support safe-navigation-operator.
74
+
75
+ ```
76
+ Pt(T) -> T? ⨉ T? # Truthy partition
77
+ Pn(T) -> T? ⨉ T? # Non-nil partition
78
+ ```
79
+
80
+ Both return a pair of optional types for non-falsy/non-nil types.
81
+
82
+ ```
83
+ Pt(false?) -> ∅ ⨉ false?
84
+ Pn(false) -> false ⨉ ∅
85
+ ```
86
+
87
+ Note that we cannot partition non-regular recursive types, but that types are prohibited in RBS.
88
+ ## Type environment
89
+
90
+ Type environment is a mapping from local variables to their types. It's extended in Steep to support overriding type of *pure* expressions.
91
+
92
+ ```
93
+ E ::= ∅
94
+ | x : T, E # Type of a local variable -- `x`
95
+ | p : T, E # Type of a pure expression -- `p`
96
+ ```
97
+
98
+ Pure expressions are defined recursively as following:
99
+
100
+ * *Value expressions* are pure
101
+ * Call expressions of *pure methods* with pure arguments are pure
102
+
103
+ Note that expression purity depends on the result of type checking of the expression because it requires to detect if a method call is *pure* or not.
104
+ ## Logic type interpreter
105
+ Logic type interpreter is an additional layer to support type narrowing. It is a function that takes an expression and it's typing, and returns a pair of type environments -- one for the case the value of the expr is *truthy* and another for the case the value is *falsy*.
106
+
107
+ ```
108
+ I(expr : T) -> E ⨉ E
109
+ ```
110
+
111
+ It takes account of assignments to local variables.
112
+
113
+ ```
114
+ I(x = y : String?) -> { x: String, y: String } ⨉ { x: nil, y: nil }
115
+ ```
116
+
117
+ It also calculates the reachability to truthy and falsy results which can be used to detect unreachable branches.
118
+ ## Narrowing syntaxes
119
+ ### Simple conditionals -- `if`, `while`, `and`, ...
120
+ Type checking those syntaxes are simple. It type checks the condition expression with conditional mode, passes the expression to logic type interpreter, uses the type environments to type check then and else clauses.
121
+
122
+ Steep also reports unreachable branch issues based on the reachability calculated by logic type interpreter.
123
+ ### `case-when`
124
+ The easier case is if `case-when` doesn't have a node just after `case` token.
125
+
126
+ ```ruby
127
+ case
128
+ when x == 1
129
+ ...
130
+ end
131
+ ```
132
+
133
+ This is the same with simple conditionals.
134
+
135
+ The difficult case is if `case-when` syntax has a node.
136
+
137
+ ```ruby
138
+ case foo()
139
+ when Integer
140
+ ...
141
+ when String
142
+ ...
143
+ end
144
+ ```
145
+
146
+ Ruby uses the `===` operator to test if the value of `foo()` matches the condition, while we don't want to type check `foo()` calls every time. It may be a pure expression, and we can give better type narrowing using the *falsy* results of predecessor `when` clauses.
147
+
148
+ So, we transform the condition expression given to the logic type interpreter to value form.
149
+
150
+ ```ruby
151
+ __case_when:x01jg9__ = foo()
152
+ if Integer === __case_when:x01jg9__
153
+ ...
154
+ elsif String === __case_when:x01jg9__
155
+ ...
156
+ end
157
+ ```
158
+
159
+ It generates a fresh local variable and assigns the expression to it. Uses the variable inside the patterns.
160
+
161
+ We also need to propagate the type of local variables which are included in the expression.
162
+
163
+ ```ruby
164
+ case x = foo()
165
+ when Integer
166
+ x.abs # x should be Integer
167
+ when String
168
+ x.upcase # x should be String
169
+ end
170
+ ```
171
+
172
+ The local variable insertion is done at the outermost non-assignment position to support the local variable propagation.
173
+
174
+ ```ruby
175
+ __case_when:x01jg9__ = foo()
176
+ if Integer === (x = __case_when:x01jg9__)
177
+ ...
178
+ elsif String === (x = __case_when:x01jg9__)
179
+ ...
180
+ end
181
+ ```
182
+
183
+ The last trick to type check case-when is *pure call* narrowing in the bodies.
184
+
185
+ ```ruby
186
+ __case_when:x01jg9__ = foo()
187
+ if Integer === (x = __case_when:x01jg9__)
188
+ foo.abs
189
+ elsif String === (x = __case_when:x01jg9__)
190
+ foo.upcase
191
+ end
192
+ ```
193
+
194
+ To support this, we propagate the type of the fresh local variable to the type of right hand side expression, if it's a pure call.
195
+
@@ -1,3 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'steep', require: false
3
+ gem 'steep', "~> 1.7.0.dev", require: false
@@ -1,36 +1,50 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- activesupport (7.0.7.2)
4
+ abbrev (0.1.2)
5
+ activesupport (7.1.3)
6
+ base64
7
+ bigdecimal
5
8
  concurrent-ruby (~> 1.0, >= 1.0.2)
9
+ connection_pool (>= 2.2.5)
10
+ drb
6
11
  i18n (>= 1.6, < 2)
7
12
  minitest (>= 5.1)
13
+ mutex_m
8
14
  tzinfo (~> 2.0)
9
15
  ast (2.4.2)
10
- concurrent-ruby (1.2.2)
11
- csv (3.2.7)
12
- ffi (1.15.5)
13
- fileutils (1.7.1)
16
+ base64 (0.2.0)
17
+ bigdecimal (3.1.6)
18
+ concurrent-ruby (1.2.3)
19
+ connection_pool (2.4.1)
20
+ csv (3.2.8)
21
+ drb (2.2.0)
22
+ ruby2_keywords
23
+ ffi (1.16.3)
24
+ fileutils (1.7.2)
14
25
  i18n (1.14.1)
15
26
  concurrent-ruby (~> 1.0)
16
- json (2.6.3)
27
+ json (2.7.1)
17
28
  language_server-protocol (3.17.0.3)
18
29
  listen (3.8.0)
19
30
  rb-fsevent (~> 0.10, >= 0.10.3)
20
31
  rb-inotify (~> 0.9, >= 0.9.10)
21
- logger (1.5.3)
22
- minitest (5.19.0)
23
- parser (3.2.2.3)
32
+ logger (1.6.0)
33
+ minitest (5.22.2)
34
+ mutex_m (0.2.0)
35
+ parser (3.3.0.5)
24
36
  ast (~> 2.4.1)
25
37
  racc
26
- racc (1.7.1)
38
+ racc (1.7.3)
27
39
  rainbow (3.1.1)
28
40
  rb-fsevent (0.11.2)
29
41
  rb-inotify (0.10.1)
30
42
  ffi (~> 1.0)
31
- rbs (3.1.3)
32
- securerandom (0.2.2)
33
- steep (1.5.3)
43
+ rbs (3.4.4)
44
+ abbrev
45
+ ruby2_keywords (0.0.5)
46
+ securerandom (0.3.1)
47
+ steep (1.7.0.dev.1)
34
48
  activesupport (>= 5.1)
35
49
  concurrent-ruby (>= 1.1.10)
36
50
  csv (>= 3.0.9)
@@ -45,20 +59,21 @@ GEM
45
59
  securerandom (>= 0.1)
46
60
  strscan (>= 1.0.0)
47
61
  terminal-table (>= 2, < 4)
48
- strscan (3.0.6)
62
+ strscan (3.1.0)
49
63
  terminal-table (3.0.2)
50
64
  unicode-display_width (>= 1.1.1, < 3)
51
65
  tzinfo (2.0.6)
52
66
  concurrent-ruby (~> 1.0)
53
- unicode-display_width (2.4.2)
67
+ unicode-display_width (2.5.0)
54
68
 
55
69
  PLATFORMS
56
70
  arm64-darwin-21
57
71
  arm64-darwin-22
72
+ arm64-darwin-23
58
73
  x86_64-linux
59
74
 
60
75
  DEPENDENCIES
61
- steep
76
+ steep (~> 1.7.0.dev)
62
77
 
63
78
  BUNDLED WITH
64
79
  2.3.15
@@ -16,7 +16,7 @@ and install the gems.
16
16
  $ bundle install
17
17
  ```
18
18
 
19
- You can install it with the gem command.
19
+ Alternatively, you can install it with the gem command.
20
20
 
21
21
  ```
22
22
  $ gem install steep
@@ -26,14 +26,14 @@ Execute the following command to confirm if the command is successfully installe
26
26
 
27
27
  ```
28
28
  $ steep version
29
- $ bundle exec steep version # When you install with bundler
29
+ $ bundle exec steep version # If you install with bundler
30
30
  ```
31
31
 
32
- We omit the `bundle exec` prefix from the following commands. Run commands with the prefix if you install Steep with bundler.
32
+ We omit the `bundle exec` prefix from the following commands. Run commands with the prefix if you installed Steep with Bundler.
33
33
 
34
34
  ## Type checking your first Ruby script
35
35
 
36
- Run steep init command to generate the configuration file, Steepfile.
36
+ Run the `steep init` command to generate the configuration file, `Steepfile`.
37
37
 
38
38
  ```
39
39
  $ steep init
@@ -54,7 +54,7 @@ Type the following Ruby code in your editor, and save it as `lib/hello.rb`.
54
54
  currencies = { US: "$", JP: "¥", UK: "£" }
55
55
  country = %w(US JP UK).sample()
56
56
 
57
- puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
57
+ puts "Hello! The price is #{currencies[country]}100. 💸"
58
58
  ```
59
59
 
60
60
  And type check it with Steep.
@@ -84,13 +84,13 @@ lib/hello.rb:4:39: [error] Cannot pass a value of type `(::String | nil)` as an
84
84
  Detected 1 problem from 1 file
85
85
  ```
86
86
 
87
- The error says that the type of the country variable causes a type error. It is expected to be a Symbol, but String or nil will be given.
87
+ The error says that the type of the country variable causes a type error. It is expected to be a symbol, but string or `nil` will be given.
88
88
 
89
89
  Let's see how we can fix the error.
90
90
 
91
91
  ## Fixing the type error
92
92
 
93
- The first step is converting the string value to a symbol. We can add to_sym call.
93
+ The first step is converting the string value to a symbol. We can add a `#to_sym` call.
94
94
 
95
95
  ```rb
96
96
  currencies = { US: "$", JP: "¥", UK: "£" }
@@ -128,7 +128,7 @@ Instead, we can simply tell the type checker that the value of the country canno
128
128
 
129
129
  The underlying type system supports flow-sensitive typing similar to TypeScript and Rust. It detects conditional expressions testing the value of a variable and propagates the knowledge that the value cannot be `nil`.
130
130
 
131
- We can fix the type error with an or construct.
131
+ We can fix the type error with an `or` construct.
132
132
 
133
133
  ```rb
134
134
  currencies = { US: "$", JP: "¥", UK: "£" }
@@ -137,7 +137,7 @@ country = %w(US JP UK).sample() or raise
137
137
  puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
138
138
  ```
139
139
 
140
- The change let the type checking succeed.
140
+ The change lets the type checking succeed.
141
141
 
142
142
  ```
143
143
  $ steep check
@@ -145,7 +145,7 @@ $ steep check
145
145
 
146
146
  .........................................................
147
147
 
148
- No type error detected. 🧉
148
+ No type error detected. 🫖
149
149
  ```
150
150
 
151
151
  The `raise` method is called when `sample()` returns `nil`. Steep can reason the possible control flow based on the semantics of or in Ruby:
@@ -161,4 +161,3 @@ There are two possibilities of the type of the result of the `sample()` call, `n
161
161
  ## Next steps
162
162
 
163
163
  This is a really quick introduction to using Steep. You may have noticed that I haven't explained anything about defining new classes or modules. See the RBS guide for more examples!
164
-