steep 1.4.0.dev.2 → 1.4.0.dev.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -2
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +7 -9
  5. data/Gemfile.steep +1 -2
  6. data/Gemfile.steep.lock +9 -10
  7. data/README.md +7 -1
  8. data/Steepfile +0 -3
  9. data/bin/rbs +0 -1
  10. data/guides/README.md +5 -0
  11. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +143 -0
  12. data/guides/src/getting-started/getting-started.md +164 -0
  13. data/guides/src/nil-optional/nil-optional.md +195 -0
  14. data/lib/steep/diagnostic/ruby.rb +79 -4
  15. data/lib/steep/drivers/check.rb +4 -4
  16. data/lib/steep/interface/block.rb +10 -0
  17. data/lib/steep/module_helper.rb +13 -11
  18. data/lib/steep/path_helper.rb +4 -0
  19. data/lib/steep/server/interaction_worker.rb +105 -92
  20. data/lib/steep/services/type_name_completion.rb +157 -0
  21. data/lib/steep/source.rb +1 -0
  22. data/lib/steep/type_construction.rb +402 -229
  23. data/lib/steep/type_inference/block_params.rb +13 -0
  24. data/lib/steep/type_inference/context.rb +3 -3
  25. data/lib/steep/type_inference/method_params.rb +42 -16
  26. data/lib/steep/type_inference/send_args.rb +79 -50
  27. data/lib/steep/type_inference/type_env.rb +7 -1
  28. data/lib/steep/version.rb +1 -1
  29. data/lib/steep.rb +1 -0
  30. data/rbs_collection.steep.lock.yaml +0 -28
  31. data/rbs_collection.steep.yaml +10 -9
  32. data/sample/lib/conference.rb +12 -0
  33. data/sample/sig/conference.rbs +5 -0
  34. data/sig/shims/language-server_protocol.rbs +12 -0
  35. data/sig/shims/parser/nodes.rbs +37 -0
  36. data/sig/shims/parser.rbs +1 -0
  37. data/sig/shims/string.rbs +4 -0
  38. data/sig/steep/ast/types/factory.rbs +10 -8
  39. data/sig/steep/diagnostic/lsp_formatter.rbs +1 -1
  40. data/sig/steep/diagnostic/ruby.rbs +38 -2
  41. data/sig/steep/drivers/check.rbs +1 -1
  42. data/sig/steep/drivers/checkfile.rbs +1 -1
  43. data/sig/steep/drivers/diagnostic_printer.rbs +1 -1
  44. data/sig/steep/drivers/watch.rbs +1 -1
  45. data/sig/steep/index/signature_symbol_provider.rbs +1 -1
  46. data/sig/steep/interface/block.rbs +2 -0
  47. data/sig/steep/interface/builder.rbs +5 -3
  48. data/sig/steep/interface/method_type.rbs +5 -3
  49. data/sig/steep/module_helper.rbs +9 -0
  50. data/sig/steep/path_helper.rbs +3 -1
  51. data/sig/steep/server/base_worker.rbs +1 -1
  52. data/sig/steep/server/interaction_worker.rbs +46 -17
  53. data/sig/steep/server/master.rbs +1 -1
  54. data/sig/steep/server/type_check_worker.rbs +7 -5
  55. data/sig/steep/server/worker_process.rbs +6 -4
  56. data/sig/steep/services/completion_provider.rbs +2 -0
  57. data/sig/steep/services/type_name_completion.rbs +122 -0
  58. data/sig/steep/type_construction.rbs +99 -30
  59. data/sig/steep/type_inference/block_params.rbs +4 -0
  60. data/sig/steep/type_inference/context.rbs +70 -22
  61. data/sig/steep/type_inference/method_params.rbs +43 -24
  62. data/sig/steep/type_inference/multiple_assignment.rbs +1 -1
  63. data/sig/steep/type_inference/send_args.rbs +13 -3
  64. data/sig/steep/typing.rbs +7 -2
  65. data/smoke/diagnostics/test_expectations.yml +1 -0
  66. data/steep.gemspec +0 -1
  67. metadata +10 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14e9c57b89791ad6d989bddd6439f1ce792ceccf8e17034a1f2ab56c85f2ca41
4
- data.tar.gz: dfbe3445dcc72a84a2d4d4e608eb5d74e9da4e1cef03f4979a44e377bf65a917
3
+ metadata.gz: ef7faa0702c27679ca29580b02273241fcfac188f2462f79a95ea26e633a231d
4
+ data.tar.gz: b9a7add36c30c5fcb76b89bb5a994fcea4e8749ddb06b9b43d5ebcada4fa51f6
5
5
  SHA512:
6
- metadata.gz: c14673012d5db258399c4f5d7bda6e3fb6aaca0dbc5c241efca52f8c4e947a5a4fd1a60a42ce6a45e87642926b6e44f65d6058bb241581e5a143251a158f383f
7
- data.tar.gz: '0889651d16f198063b243aef34cb300325fb04dcbb7165f0d8c972d7f0f2076d86af77393c8c3f0f216d1a42f6fa8bd2c4cbf78d3e0c106d8c7f883d96099839'
6
+ metadata.gz: f5b67c4723ec0aec87a215eb874f73ebc62de658b50cc4118399691f9451d0d540ea4640623d94771430839a40399097da2310c98f40e6c50669ceba8d1de026
7
+ data.tar.gz: d6da7929577c2071c43b6c13ca28b2b0017626ffb760618b2a5551d1824c1c0ba3cbad6c915c522ea1d784caa4bd13ac5364e052972a4573a717c72fd029528d
@@ -15,7 +15,7 @@ jobs:
15
15
  - "3.0"
16
16
  - "3.1"
17
17
  - "3.2"
18
- - "master-nightly-focal"
18
+ # - "master-nightly-focal"
19
19
  task:
20
20
  - test
21
21
  - test:output
@@ -30,6 +30,5 @@ jobs:
30
30
  git config --global --add safe.directory /__w/steep/steep
31
31
  ruby -v
32
32
  gem install bundler
33
- bundle install --jobs 4 --retry 3
34
33
  bin/setup
35
34
  bundle exec rake ${{matrix.task}}
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ gemspec
6
6
  gem "rbs", "~> 3.0.0"
7
7
 
8
8
  gem "rake"
9
- gem "minitest", "~> 5.17"
9
+ gem "minitest", "~> 5.18"
10
10
  gem "minitest-hooks"
11
11
  group :stackprof, optional: true do
12
12
  gem "stackprof"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- steep (1.4.0.dev.2)
4
+ steep (1.4.0.dev.3)
5
5
  activesupport (>= 5.1)
6
6
  csv (>= 3.0.9)
7
7
  fileutils (>= 1.1.0)
@@ -11,7 +11,6 @@ PATH
11
11
  logger (>= 1.3.0)
12
12
  parallel (>= 1.0.0)
13
13
  parser (>= 3.1)
14
- pathname (>= 0.2.1)
15
14
  rainbow (>= 2.2.2, < 4.0)
16
15
  rbs (>= 2.8.0)
17
16
  securerandom (>= 0.1)
@@ -21,7 +20,7 @@ PATH
21
20
  GEM
22
21
  remote: https://rubygems.org/
23
22
  specs:
24
- activesupport (7.0.4.2)
23
+ activesupport (7.0.4.3)
25
24
  concurrent-ruby (~> 1.0, >= 1.0.2)
26
25
  i18n (>= 1.6, < 2)
27
26
  minitest (>= 5.1)
@@ -33,7 +32,7 @@ GEM
33
32
  debase-ruby_core_source (>= 0.10.12)
34
33
  debase-ruby_core_source (3.2.0)
35
34
  ffi (1.15.5)
36
- fileutils (1.7.0)
35
+ fileutils (1.7.1)
37
36
  i18n (1.12.0)
38
37
  concurrent-ruby (~> 1.0)
39
38
  json (2.6.3)
@@ -42,21 +41,20 @@ GEM
42
41
  rb-fsevent (~> 0.10, >= 0.10.3)
43
42
  rb-inotify (~> 0.9, >= 0.9.10)
44
43
  logger (1.5.3)
45
- minitest (5.17.0)
44
+ minitest (5.18.0)
46
45
  minitest-hooks (1.5.0)
47
46
  minitest (> 5.3)
48
47
  minitest-slow_test (0.2.0)
49
48
  minitest (>= 5.0)
50
49
  parallel (1.22.1)
51
- parser (3.2.1.0)
50
+ parser (3.2.2.0)
52
51
  ast (~> 2.4.1)
53
- pathname (0.2.1)
54
52
  rainbow (3.1.1)
55
53
  rake (13.0.6)
56
54
  rb-fsevent (0.11.2)
57
55
  rb-inotify (0.10.1)
58
56
  ffi (~> 1.0)
59
- rbs (3.0.1)
57
+ rbs (3.0.4)
60
58
  ruby-debug-ide (0.7.3)
61
59
  rake (>= 0.8.1)
62
60
  securerandom (0.2.2)
@@ -73,7 +71,7 @@ PLATFORMS
73
71
 
74
72
  DEPENDENCIES
75
73
  debase (>= 0.2.5.beta2)
76
- minitest (~> 5.17)
74
+ minitest (~> 5.18)
77
75
  minitest-hooks
78
76
  minitest-slow_test
79
77
  rake
data/Gemfile.steep CHANGED
@@ -1,4 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'steep', '~> 1.4.0.dev'
4
- gem "rbs", "~> 3.0.0.dev"
3
+ gem 'steep', '~> 1.4.0.dev', require: false
data/Gemfile.steep.lock CHANGED
@@ -1,36 +1,36 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- activesupport (7.0.4)
4
+ activesupport (7.0.4.2)
5
5
  concurrent-ruby (~> 1.0, >= 1.0.2)
6
6
  i18n (>= 1.6, < 2)
7
7
  minitest (>= 5.1)
8
8
  tzinfo (~> 2.0)
9
9
  ast (2.4.2)
10
- concurrent-ruby (1.1.10)
10
+ concurrent-ruby (1.2.2)
11
11
  csv (3.2.6)
12
12
  ffi (1.15.5)
13
13
  fileutils (1.7.0)
14
14
  i18n (1.12.0)
15
15
  concurrent-ruby (~> 1.0)
16
16
  json (2.6.3)
17
- language_server-protocol (3.17.0.2)
18
- listen (3.7.1)
17
+ language_server-protocol (3.17.0.3)
18
+ listen (3.8.0)
19
19
  rb-fsevent (~> 0.10, >= 0.10.3)
20
20
  rb-inotify (~> 0.9, >= 0.9.10)
21
21
  logger (1.5.3)
22
22
  minitest (5.17.0)
23
23
  parallel (1.22.1)
24
- parser (3.2.0.0)
24
+ parser (3.2.1.0)
25
25
  ast (~> 2.4.1)
26
26
  pathname (0.2.1)
27
27
  rainbow (3.1.1)
28
28
  rb-fsevent (0.11.2)
29
29
  rb-inotify (0.10.1)
30
30
  ffi (~> 1.0)
31
- rbs (3.0.0.dev.1)
31
+ rbs (3.0.4)
32
32
  securerandom (0.2.2)
33
- steep (1.4.0.dev.1)
33
+ steep (1.4.0.dev.2)
34
34
  activesupport (>= 5.1)
35
35
  csv (>= 3.0.9)
36
36
  fileutils (>= 1.1.0)
@@ -46,10 +46,10 @@ GEM
46
46
  securerandom (>= 0.1)
47
47
  strscan (>= 1.0.0)
48
48
  terminal-table (>= 2, < 4)
49
- strscan (3.0.5)
49
+ strscan (3.0.6)
50
50
  terminal-table (3.0.2)
51
51
  unicode-display_width (>= 1.1.1, < 3)
52
- tzinfo (2.0.5)
52
+ tzinfo (2.0.6)
53
53
  concurrent-ruby (~> 1.0)
54
54
  unicode-display_width (2.4.2)
55
55
 
@@ -58,7 +58,6 @@ PLATFORMS
58
58
  arm64-darwin-22
59
59
 
60
60
  DEPENDENCIES
61
- rbs (~> 3.0.0.dev)
62
61
  steep (~> 1.4.0.dev)
63
62
 
64
63
  BUNDLED WITH
data/README.md CHANGED
@@ -201,13 +201,19 @@ Generally, these are by our design.
201
201
 
202
202
  `rbs prototype` offers options: `rbi` to generate prototype from Sorbet RBI and `runtime` to generate from runtime API.
203
203
 
204
+ ## Guides
205
+
206
+ There are some guides in the `guide` directory. I know we need more comprehensive set of documentations. Just started writing docs.
207
+
208
+ * [Guides](guides)
209
+
204
210
  ## Examples
205
211
 
206
212
  You can find examples in `smoke` directory.
207
213
 
208
214
  ## IDEs
209
215
 
210
- Steep implements some of the Language Server Protocol features.
216
+ Steep implements some of the Language Server Protocol features.
211
217
  - For **VSCode** please install [the plugin](https://github.com/soutaro/steep-vscode)
212
218
  - For **SublimeText** please install [LSP](https://github.com/sublimelsp/LSP) package and follow [instructions](https://lsp.sublimetext.io/language_servers/#steep)
213
219
 
data/Steepfile CHANGED
@@ -27,8 +27,6 @@ target :app do
27
27
 
28
28
  library(
29
29
  "rdoc",
30
- "set",
31
- "pathname",
32
30
  "monitor",
33
31
  "tsort",
34
32
  "uri",
@@ -39,6 +37,5 @@ target :app do
39
37
  'find',
40
38
  'digest',
41
39
  "optparse",
42
- "securerandom"
43
40
  )
44
41
  end
data/bin/rbs CHANGED
@@ -18,4 +18,3 @@ else
18
18
  fi
19
19
 
20
20
  exec $RBS $@
21
-
data/guides/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Steep Guides
2
+
3
+ * [Getting Started with Steep in 5 minutes](src/getting-started/getting-started.md)
4
+ * [Using RBS from gem_rbs_collection](src/gem-rbs-collection/gem-rbs-collection.md)
5
+ * [`nil` and Optional types](src/nil-optional/nil-optional.md)
@@ -0,0 +1,143 @@
1
+ # Using RBS from gem_rbs_collection
2
+
3
+ gem_rbs_collection is a repository of type definitions of gems that are managed by the community. You may find the type definition of a gem that ships without RBS type definitions.
4
+
5
+ To use RBS files from the repository, you can use rbs-collection subcommand. This guide explains how the command works.
6
+
7
+ ## Quick start
8
+
9
+ Run rbs-collection-init to start setup.
10
+
11
+ ```
12
+ $ rbs collection init
13
+ ```
14
+
15
+ You have to edit the generated `rbs_collection.yaml` file. Add a few lines under gems section.
16
+
17
+ ```yaml
18
+ # Download sources
19
+ sources:
20
+ - name: ruby/gem_rbs_collection
21
+ remote: https://github.com/ruby/gem_rbs_collection.git
22
+ revision: main
23
+ repo_dir: gems
24
+
25
+ # A directory to install the downloaded RBSs
26
+ path: .gem_rbs_collection
27
+
28
+ gems:
29
+ # Skip loading rbs gem's RBS.
30
+ # It's unnecessary if you don't use rbs as a library.
31
+ - name: rbs
32
+ ignore: true
33
+ - name: steep
34
+ ignore: true
35
+ - name: rbs_rails # Add these lines if you use rbs_rails
36
+ ignore: true
37
+ - name: rbs_protobuf # Add these lines if you use rbs_protobuf
38
+ ignore: true
39
+ ```
40
+
41
+ Once you save the file, run the install command.
42
+
43
+ ```
44
+ $ rbs collection install
45
+ ```
46
+
47
+ That generates `rbs_collection.lock.yaml` and downloads the RBS files from the git repository.
48
+
49
+ Note that rbs-collection automatically downloads RBS files of gems included in your Bundler environment. You don't have to write all of the gems in your `Gemfile` in `rbs_collection.yaml`.
50
+
51
+ Finally, we recommend adding the `rbs_collection.yaml` and `rbs_collection.lock.yaml` to your repository, and ignoring `.gem_rbs_collection` directory.
52
+
53
+ ```
54
+ $ git add rbs_collection.yaml
55
+ $ git add rbs_collection.lock.yaml
56
+ $ echo /.gem_rbs_collection >> .gitignore
57
+ $ git commit -m "Set up rbs-collection"
58
+ ```
59
+
60
+ ## Updating RBS files
61
+
62
+ You may want to run rbs-collection-update to update the contents of `rbs_collection.lock.yaml`, when you add a new gem or some gems are updated.
63
+
64
+ You also need to run rbs-collection-update when the RBS files in the source repository are updated. The type definitions are updated with bug-fixes or improvements, and you need to update to apply the changes to your app.
65
+
66
+ ## Using rbs-collection with Steep
67
+
68
+ Steep automatically reads `rb_collection.yaml`. You can use Steep immediately without any modifications.
69
+
70
+ ```
71
+ $ bin/steep project # Show dependencies detected by Steep
72
+ $ bin/steep check # Run type check
73
+ ```
74
+
75
+ ## Migration
76
+
77
+ If you have used older versions of Steep or RBS, you may have configured libraries manually.
78
+
79
+ * You have library calls in `Steepfile`
80
+ * You have git submodules in your git repository to download gem_rbs_collection
81
+
82
+ These are steps to migrate to rbs-collection.
83
+
84
+ 1. Remove unnecessary library configuration
85
+ 2. Set up rbs-collection
86
+ 3. Validate the configuration
87
+ 4. Delete submodule
88
+
89
+ ### 1. Remove unnecessary library configurations
90
+
91
+ You may have `#library` calls in your Steepfile, or something equivalent in your scripts. We can group the configured libraries into three groups.
92
+
93
+ 1. Gems that is managed by Bundler
94
+ 2. Standard libraries (non-gem libs, default gems, and bundled gems)
95
+ * 2-1) That is a transitive dependency from libraries in group 1
96
+ * 2-2) That is included in Gemfile.lock
97
+ * 2-3) Implicitly installed gems – not included in Gemfile
98
+
99
+ You can delete library configs of 1, 2-1, and 2-2. So, you have to keep the configurations of libraries in 2-3.
100
+
101
+ Practically, you can remove all library configs and `#library` calls in `Steepfile`, go step 2, run steep check to test, and restore the configs of libraries if error is detected.
102
+
103
+ ### 2. Set up rbs-collection
104
+
105
+ See the quick start section above!
106
+
107
+ ### 3. Validate the configuration
108
+
109
+ Run steep check to validate the configuration.
110
+
111
+ ```
112
+ $ bin/steep check
113
+ ```
114
+
115
+ You may see the `UnknownTypeName` error if some libraries are missing. Or some errors that implies duplication or inconsistency of methods/class/module definitions, that may be caused by loading RBS files of a library twice.
116
+
117
+ Running steep project may help you too. It shows the source files and library directories recognized by Steep.
118
+
119
+ ```
120
+ $ bin/steep project
121
+ ```
122
+
123
+ Note that the type errors detected may change after migrating to rbs-collection, because it typically includes updating to newer versions of RBS files.
124
+
125
+ ### 4. Delete submodule
126
+
127
+ After you confirmed everything is working correctly, you can delete the submodule. deinit the submodule, remove the directory using git-rm, and delete $GIT_DIR/modules/<name>/.
128
+
129
+ ## What is the rbs_collection.yaml file?
130
+
131
+ The file mainly defines three properties – sources, gems and path. Sources is essential when you want to create a new RBS file repository, usually for RBS files of the private gems.
132
+
133
+ Another trick is ignoring RBS files of type checker toolchain. RBS and Steep ships with their own RBS files. However, these RBS files may be unnecessary for you, unless you are not a type checking toolchain developer. It requires some additional gems and RBS files. So adding ignore: true is recommended for the gems.
134
+
135
+ It seems like we need to add a feature to skip loading RBS files automatically and use the feature for RBS, Steep, and RBS Rails. It looks weird that the gems section is only used to ignore gems.
136
+
137
+ ## Versions of RBS files in gem_rbs_collection
138
+
139
+ Gem versions in rbs-collection are relatively loosely managed. If a gem is found but the version is different, rbs-collection simply uses the incorrect version.
140
+
141
+ It will load RBS files of activerecord/6.1 even if your Gemfile specifies activerecord-7.0.4. This is by design with an assumption that having RBS files with some incompatibility is better than having nothing. We see most APIs are compatible even after major version upgrade. Dropping everything for minor API incompatibilities would not make much sense.
142
+
143
+ That behavior will change in future versions when we see that the assumption is not reasonable and we have better coverage.
@@ -0,0 +1,164 @@
1
+ # Getting Started with Steep in 5 minutes
2
+
3
+ ## Installing Steep
4
+
5
+ Add the lines to Gemfile:
6
+
7
+ ```rb
8
+ group :development do
9
+ gem "steep", require: false
10
+ end
11
+ ```
12
+
13
+ and install the gems.
14
+
15
+ ```
16
+ $ bundle install
17
+ ```
18
+
19
+ You can install it with the gem command.
20
+
21
+ ```
22
+ $ gem install steep
23
+ ```
24
+
25
+ Execute the following command to confirm if the command is successfully installed.
26
+
27
+ ```
28
+ $ steep version
29
+ $ bundle exec steep version # When you install with bundler
30
+ ```
31
+
32
+ We omit the `bundle exec` prefix from the following commands. Run commands with the prefix if you install Steep with bundler.
33
+
34
+ ## Type checking your first Ruby script
35
+
36
+ Run steep init command to generate the configuration file, Steepfile.
37
+
38
+ ```
39
+ $ steep init
40
+ ```
41
+
42
+ Open `Steepfile` in your text editor, and replace the content with the following lines:
43
+
44
+ ```rb
45
+ target :lib do
46
+ signature "sig"
47
+ check "lib"
48
+ end
49
+ ```
50
+
51
+ Type the following Ruby code in your editor, and save it as `lib/hello.rb`.
52
+
53
+ ```rb
54
+ currencies = { US: "$", JP: "¥", UK: "£" }
55
+ country = %w(US JP UK).sample()
56
+
57
+ puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
58
+ ```
59
+
60
+ And type check it with Steep.
61
+
62
+ ```
63
+ $ steep check
64
+ ```
65
+
66
+ The output will report a type error.
67
+
68
+ ```
69
+ # Type checking files:
70
+
71
+ ........................................................F
72
+
73
+ lib/hello.rb:4:39: [error] Cannot pass a value of type `(::String | nil)` as an argument of type `::Symbol`
74
+ │ (::String | nil) <: ::Symbol
75
+ │ ::String <: ::Symbol
76
+ │ ::Object <: ::Symbol
77
+ │ ::BasicObject <: ::Symbol
78
+
79
+ │ Diagnostic ID: Ruby::ArgumentTypeMismatch
80
+
81
+ └ puts "Hello! The price is #{currencies[country]}100. 💸"
82
+ ~~~~~~~
83
+
84
+ Detected 1 problem from 1 file
85
+ ```
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.
88
+
89
+ Let's see how we can fix the error.
90
+
91
+ ## Fixing the type error
92
+
93
+ The first step is converting the string value to a symbol. We can add to_sym call.
94
+
95
+ ```rb
96
+ currencies = { US: "$", JP: "¥", UK: "£" }
97
+ country = %w(US JP UK).sample()
98
+
99
+ puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
100
+ ```
101
+
102
+ The `#to_sym` call will convert a string into a symbol. Does it solve the problem??
103
+
104
+ ```
105
+ $ steep check
106
+ # Type checking files:
107
+
108
+ ........................................................F
109
+
110
+ lib/hello.rb:4:47: [error] Type `(::String | nil)` does not have method `to_sym`
111
+ │ Diagnostic ID: Ruby::NoMethod
112
+
113
+ └ puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
114
+ ~~~~~~
115
+
116
+ Detected 1 problem from 1 file
117
+ ```
118
+
119
+ It detects another problem. The first error was `Ruby::ArgumentTypeMismatch`, but the new error is `Ruby::NoMethod`. The value of `country` may be `nil`, and it doesn't have the `#to_sym` method.
120
+
121
+ This would be annoying, but one of the most common sources of a type error. The value of an expression may be `nil` unexpectedly, and using the value of the expression may cause an error.
122
+
123
+ In this case, the `sample()` call introduces the `nil`. `Array#sample()` returns `nil` when the array is empty. We know the receiver of the `sample` call cannot be `nil`, because it is an array literal. But the type checker doesn't know of it. The source code detects the `Array#sample()` method is called, but it ignores the fact that the receiver cannot be empty.
124
+
125
+ Instead, we can simply tell the type checker that the value of the country cannot be `nil`.
126
+
127
+ # Satisfying the type checker by adding a guard
128
+
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
+
131
+ We can fix the type error with an or construct.
132
+
133
+ ```rb
134
+ currencies = { US: "$", JP: "¥", UK: "£" }
135
+ country = %w(US JP UK).sample() or raise
136
+
137
+ puts "Hello! The price is #{currencies[country.to_sym]}100. 💸"
138
+ ```
139
+
140
+ The change let the type checking succeed.
141
+
142
+ ```
143
+ $ steep check
144
+ # Type checking files:
145
+
146
+ .........................................................
147
+
148
+ No type error detected. 🧉
149
+ ```
150
+
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:
152
+
153
+ * The value of `country` is the return value of `sample()` method call
154
+ * The value of `country` may be `nil`
155
+ * If the value of `country` is `nil`, the right hand side of the or is evaluated
156
+ * It calls the `raise` method, which results in an exception and jumps to somewhere
157
+ * So, when the execution continues to the puts line, the value of `country` cannot be `nil`
158
+
159
+ There are two possibilities of the type of the result of the `sample()` call, `nil` or a string. We humans can reason that we can safely ignore the case of `nil`. But, the type checker cannot. We have to add `or raise` to tell the type checker it can stop considering the case of `nil` safely.
160
+
161
+ ## Next steps
162
+
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
+