steep-activesupport-4 1.9.3

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +1032 -0
  5. data/LICENSE +21 -0
  6. data/README.md +260 -0
  7. data/Rakefile +227 -0
  8. data/Steepfile +68 -0
  9. data/bin/console +14 -0
  10. data/bin/generate-diagnostics-docs.rb +112 -0
  11. data/bin/mem_graph.rb +67 -0
  12. data/bin/mem_prof.rb +102 -0
  13. data/bin/output_rebaseline.rb +34 -0
  14. data/bin/output_test.rb +60 -0
  15. data/bin/rbs +20 -0
  16. data/bin/rbs-inline +19 -0
  17. data/bin/setup +9 -0
  18. data/bin/stackprof_test.rb +19 -0
  19. data/bin/steep +19 -0
  20. data/bin/steep-check.rb +251 -0
  21. data/bin/steep-prof +16 -0
  22. data/doc/narrowing.md +195 -0
  23. data/doc/shape.md +194 -0
  24. data/exe/steep +18 -0
  25. data/guides/README.md +5 -0
  26. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
  27. data/guides/src/getting-started/getting-started.md +163 -0
  28. data/guides/src/nil-optional/nil-optional.md +195 -0
  29. data/lib/steep/annotation_parser.rb +199 -0
  30. data/lib/steep/ast/annotation/collection.rb +172 -0
  31. data/lib/steep/ast/annotation.rb +137 -0
  32. data/lib/steep/ast/builtin.rb +104 -0
  33. data/lib/steep/ast/ignore.rb +148 -0
  34. data/lib/steep/ast/node/type_application.rb +88 -0
  35. data/lib/steep/ast/node/type_assertion.rb +81 -0
  36. data/lib/steep/ast/types/any.rb +35 -0
  37. data/lib/steep/ast/types/boolean.rb +45 -0
  38. data/lib/steep/ast/types/bot.rb +35 -0
  39. data/lib/steep/ast/types/class.rb +43 -0
  40. data/lib/steep/ast/types/factory.rb +557 -0
  41. data/lib/steep/ast/types/helper.rb +40 -0
  42. data/lib/steep/ast/types/instance.rb +42 -0
  43. data/lib/steep/ast/types/intersection.rb +93 -0
  44. data/lib/steep/ast/types/literal.rb +59 -0
  45. data/lib/steep/ast/types/logic.rb +84 -0
  46. data/lib/steep/ast/types/name.rb +128 -0
  47. data/lib/steep/ast/types/nil.rb +41 -0
  48. data/lib/steep/ast/types/proc.rb +117 -0
  49. data/lib/steep/ast/types/record.rb +79 -0
  50. data/lib/steep/ast/types/self.rb +43 -0
  51. data/lib/steep/ast/types/shared_instance.rb +11 -0
  52. data/lib/steep/ast/types/top.rb +35 -0
  53. data/lib/steep/ast/types/tuple.rb +60 -0
  54. data/lib/steep/ast/types/union.rb +97 -0
  55. data/lib/steep/ast/types/var.rb +65 -0
  56. data/lib/steep/ast/types/void.rb +35 -0
  57. data/lib/steep/cli.rb +401 -0
  58. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  59. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  60. data/lib/steep/diagnostic/helper.rb +18 -0
  61. data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
  62. data/lib/steep/diagnostic/result_printer2.rb +48 -0
  63. data/lib/steep/diagnostic/ruby.rb +1221 -0
  64. data/lib/steep/diagnostic/signature.rb +570 -0
  65. data/lib/steep/drivers/annotations.rb +52 -0
  66. data/lib/steep/drivers/check.rb +339 -0
  67. data/lib/steep/drivers/checkfile.rb +210 -0
  68. data/lib/steep/drivers/diagnostic_printer.rb +105 -0
  69. data/lib/steep/drivers/init.rb +66 -0
  70. data/lib/steep/drivers/langserver.rb +56 -0
  71. data/lib/steep/drivers/print_project.rb +113 -0
  72. data/lib/steep/drivers/stats.rb +203 -0
  73. data/lib/steep/drivers/utils/driver_helper.rb +143 -0
  74. data/lib/steep/drivers/utils/jobs_option.rb +26 -0
  75. data/lib/steep/drivers/vendor.rb +27 -0
  76. data/lib/steep/drivers/watch.rb +194 -0
  77. data/lib/steep/drivers/worker.rb +58 -0
  78. data/lib/steep/equatable.rb +23 -0
  79. data/lib/steep/expectations.rb +228 -0
  80. data/lib/steep/index/rbs_index.rb +350 -0
  81. data/lib/steep/index/signature_symbol_provider.rb +185 -0
  82. data/lib/steep/index/source_index.rb +167 -0
  83. data/lib/steep/interface/block.rb +103 -0
  84. data/lib/steep/interface/builder.rb +843 -0
  85. data/lib/steep/interface/function.rb +1090 -0
  86. data/lib/steep/interface/method_type.rb +330 -0
  87. data/lib/steep/interface/shape.rb +239 -0
  88. data/lib/steep/interface/substitution.rb +159 -0
  89. data/lib/steep/interface/type_param.rb +115 -0
  90. data/lib/steep/located_value.rb +20 -0
  91. data/lib/steep/method_name.rb +42 -0
  92. data/lib/steep/module_helper.rb +24 -0
  93. data/lib/steep/node_helper.rb +273 -0
  94. data/lib/steep/path_helper.rb +30 -0
  95. data/lib/steep/project/dsl.rb +268 -0
  96. data/lib/steep/project/group.rb +31 -0
  97. data/lib/steep/project/options.rb +63 -0
  98. data/lib/steep/project/pattern.rb +59 -0
  99. data/lib/steep/project/target.rb +92 -0
  100. data/lib/steep/project.rb +78 -0
  101. data/lib/steep/rake_task.rb +132 -0
  102. data/lib/steep/range_extension.rb +29 -0
  103. data/lib/steep/server/base_worker.rb +97 -0
  104. data/lib/steep/server/change_buffer.rb +73 -0
  105. data/lib/steep/server/custom_methods.rb +77 -0
  106. data/lib/steep/server/delay_queue.rb +45 -0
  107. data/lib/steep/server/interaction_worker.rb +492 -0
  108. data/lib/steep/server/lsp_formatter.rb +455 -0
  109. data/lib/steep/server/master.rb +912 -0
  110. data/lib/steep/server/target_group_files.rb +205 -0
  111. data/lib/steep/server/type_check_controller.rb +366 -0
  112. data/lib/steep/server/type_check_worker.rb +303 -0
  113. data/lib/steep/server/work_done_progress.rb +64 -0
  114. data/lib/steep/server/worker_process.rb +176 -0
  115. data/lib/steep/services/completion_provider.rb +802 -0
  116. data/lib/steep/services/content_change.rb +61 -0
  117. data/lib/steep/services/file_loader.rb +74 -0
  118. data/lib/steep/services/goto_service.rb +441 -0
  119. data/lib/steep/services/hover_provider/rbs.rb +88 -0
  120. data/lib/steep/services/hover_provider/ruby.rb +221 -0
  121. data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
  122. data/lib/steep/services/path_assignment.rb +46 -0
  123. data/lib/steep/services/signature_help_provider.rb +202 -0
  124. data/lib/steep/services/signature_service.rb +428 -0
  125. data/lib/steep/services/stats_calculator.rb +68 -0
  126. data/lib/steep/services/type_check_service.rb +394 -0
  127. data/lib/steep/services/type_name_completion.rb +236 -0
  128. data/lib/steep/signature/validator.rb +651 -0
  129. data/lib/steep/source/ignore_ranges.rb +69 -0
  130. data/lib/steep/source.rb +691 -0
  131. data/lib/steep/subtyping/cache.rb +30 -0
  132. data/lib/steep/subtyping/check.rb +1113 -0
  133. data/lib/steep/subtyping/constraints.rb +341 -0
  134. data/lib/steep/subtyping/relation.rb +101 -0
  135. data/lib/steep/subtyping/result.rb +324 -0
  136. data/lib/steep/subtyping/variable_variance.rb +89 -0
  137. data/lib/steep/test.rb +9 -0
  138. data/lib/steep/thread_waiter.rb +43 -0
  139. data/lib/steep/type_construction.rb +5183 -0
  140. data/lib/steep/type_inference/block_params.rb +416 -0
  141. data/lib/steep/type_inference/case_when.rb +303 -0
  142. data/lib/steep/type_inference/constant_env.rb +56 -0
  143. data/lib/steep/type_inference/context.rb +195 -0
  144. data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
  145. data/lib/steep/type_inference/method_call.rb +193 -0
  146. data/lib/steep/type_inference/method_params.rb +531 -0
  147. data/lib/steep/type_inference/multiple_assignment.rb +194 -0
  148. data/lib/steep/type_inference/send_args.rb +712 -0
  149. data/lib/steep/type_inference/type_env.rb +341 -0
  150. data/lib/steep/type_inference/type_env_builder.rb +138 -0
  151. data/lib/steep/typing.rb +321 -0
  152. data/lib/steep/version.rb +3 -0
  153. data/lib/steep.rb +369 -0
  154. data/manual/annotations.md +181 -0
  155. data/manual/ignore.md +20 -0
  156. data/manual/ruby-diagnostics.md +1879 -0
  157. data/sample/Steepfile +22 -0
  158. data/sample/lib/conference.rb +49 -0
  159. data/sample/lib/length.rb +35 -0
  160. data/sample/sig/conference.rbs +42 -0
  161. data/sample/sig/generics.rbs +15 -0
  162. data/sample/sig/length.rbs +34 -0
  163. data/steep-activesupport-4.gemspec +55 -0
  164. metadata +437 -0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Soutaro Matsumoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,260 @@
1
+ > [!IMPORTANT]
2
+ > This is a fork of the [Steep gem](https://github.com/soutaro/steep). This fork exists primarily to support a legacy system that relies on ActiveSupport 4.2.
3
+
4
+ ---
5
+
6
+ # Steep - Gradual Typing for Ruby
7
+
8
+ ## Installation
9
+
10
+ Install via RubyGems.
11
+
12
+ $ gem install steep
13
+
14
+ ### Requirements
15
+
16
+ Steep requires Ruby 2.6 or later.
17
+
18
+ ## Usage
19
+
20
+ Steep does not infer types from Ruby programs, but requires declaring types and writing annotations.
21
+ You have to go on the following three steps.
22
+
23
+ ### 0. `steep init`
24
+
25
+ Run `steep init` to generate a configuration file.
26
+
27
+ ```
28
+ $ steep init # Generates Steepfile
29
+ ```
30
+
31
+ Edit the `Steepfile`:
32
+
33
+ ```rb
34
+ target :app do
35
+ check "lib"
36
+ signature "sig"
37
+
38
+ library "pathname"
39
+ end
40
+ ```
41
+
42
+ ### 1. Declare Types
43
+
44
+ Declare types in `.rbs` files in `sig` directory.
45
+
46
+ ```
47
+ class Person
48
+ @name: String
49
+ @contacts: Array[Email | Phone]
50
+
51
+ def initialize: (name: String) -> untyped
52
+ def name: -> String
53
+ def contacts: -> Array[Email | Phone]
54
+ def guess_country: -> (String | nil)
55
+ end
56
+
57
+ class Email
58
+ @address: String
59
+
60
+ def initialize: (address: String) -> untyped
61
+ def address: -> String
62
+ end
63
+
64
+ class Phone
65
+ @country: String
66
+ @number: String
67
+
68
+ def initialize: (country: String, number: String) -> untyped
69
+ def country: -> String
70
+ def number: -> String
71
+
72
+ def self.countries: -> Hash[String, String]
73
+ end
74
+ ```
75
+
76
+ - You can use simple _generics_, like `Hash[String, String]`.
77
+ - You can use _union types_, like `Email | Phone`.
78
+ - You have to declare not only public methods but also private methods and instance variables.
79
+ - You can declare _singleton methods_, like `self.countries`.
80
+ - There is `nil` type to represent _nullable_ types.
81
+
82
+ ### 2. Write Ruby Code
83
+
84
+ Write Ruby code with annotations.
85
+
86
+ ```rb
87
+ class Person
88
+ # `@dynamic` annotation is to tell steep that
89
+ # the `name` and `contacts` methods are defined without def syntax.
90
+ # (Steep can skip checking if the methods are implemented.)
91
+
92
+ # @dynamic name, contacts
93
+ attr_reader :name
94
+ attr_reader :contacts
95
+
96
+ def initialize(name:)
97
+ @name = name
98
+ @contacts = []
99
+ end
100
+
101
+ def guess_country()
102
+ contacts.map do |contact|
103
+ # With case expression, simple type-case is implemented.
104
+ # `contact` has type of `Phone | Email` but in the `when` clause, contact has type of `Phone`.
105
+ case contact
106
+ when Phone
107
+ contact.country
108
+ end
109
+ end.compact.first
110
+ end
111
+ end
112
+
113
+ class Email
114
+ # @dynamic address
115
+ attr_reader :address
116
+
117
+ def initialize(address:)
118
+ @address = address
119
+ end
120
+
121
+ def ==(other)
122
+ # `other` has type of `untyped`, which means type checking is skipped.
123
+ # No type errors can be detected in this method.
124
+ other.is_a?(self.class) && other.address == address
125
+ end
126
+
127
+ def hash
128
+ [self.class, address].hash
129
+ end
130
+ end
131
+
132
+ class Phone
133
+ # @dynamic country, number
134
+ attr_reader :country, :number
135
+
136
+ def initialize(country:, number:)
137
+ @country = country
138
+ @number = number
139
+ end
140
+
141
+ def ==(other)
142
+ # You cannot use `case` for type case because `other` has type of `untyped`, not a union type.
143
+ # You have to explicitly declare the type of `other` in `if` expression.
144
+
145
+ if other.is_a?(Phone)
146
+ # @type var other: Phone
147
+ other.country == country && other.number == number
148
+ end
149
+ end
150
+
151
+ def hash
152
+ [self.class, country, number].hash
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### 3. Type Check
158
+
159
+ Run `steep check` command to type check. 💡
160
+
161
+ ```
162
+ $ steep check
163
+ lib/phone.rb:46:0: MethodDefinitionMissing: module=::Phone, method=self.countries (class Phone)
164
+ ```
165
+
166
+ You now find `Phone.countries` method is not implemented yet. 🙃
167
+
168
+ ## Prototyping signature
169
+
170
+ You can use `rbs prototype` command to generate a signature declaration.
171
+
172
+ ```
173
+ $ rbs prototype rb lib/person.rb lib/email.rb lib/phone.rb
174
+ class Person
175
+ @name: untyped
176
+ @contacts: Array[untyped]
177
+ def initialize: (name: untyped) -> Array[untyped]
178
+ def guess_country: () -> untyped
179
+ end
180
+
181
+ class Email
182
+ @address: untyped
183
+ def initialize: (address: untyped) -> untyped
184
+ def ==: (untyped) -> untyped
185
+ def hash: () -> untyped
186
+ end
187
+
188
+ class Phone
189
+ @country: untyped
190
+ @number: untyped
191
+ def initialize: (country: untyped, number: untyped) -> untyped
192
+ def ==: (untyped) -> void
193
+ def hash: () -> untyped
194
+ end
195
+ ```
196
+
197
+ It prints all methods, classes, instance variables, and constants.
198
+ It can be a good starting point to writing signatures.
199
+
200
+ Because it just prints all `def`s, you may find some odd points:
201
+
202
+ - The type of `initialize` in `Person` looks strange.
203
+ - There are no `attr_reader` methods extracted.
204
+
205
+ Generally, these are by our design.
206
+
207
+ `rbs prototype` offers options: `rbi` to generate prototype from Sorbet RBI and `runtime` to generate from runtime API.
208
+
209
+ ## Docs
210
+
211
+ There are some documents in the `manul` and `guide` directories.
212
+
213
+ - [Guides](guides)
214
+ - [Manual](manual)
215
+
216
+ The `doc` directory contains a few internal design docs.
217
+
218
+ - [Internal docs](doc)
219
+
220
+ ## Examples
221
+
222
+ You can find examples in `smoke` directory.
223
+
224
+ ## IDEs
225
+
226
+ Steep implements some of the Language Server Protocol features.
227
+
228
+ - For **VSCode** please install [the plugin](https://github.com/soutaro/steep-vscode).
229
+ - For **SublimeText** please install [LSP](https://github.com/sublimelsp/LSP) package and follow [instructions](https://lsp.sublimetext.io/language_servers/#steep).
230
+ - For **Vim** or **Neovim** please install [ALE](https://github.com/dense-analysis/ale?tab=readme-ov-file#asynchronous-lint-engine). You may want to `let g:ale_ruby_steep_executable = 'bundle'` to use your bundled `steep` version.
231
+
232
+ Other LSP supporting tools may work with Steep where it starts the server as `steep langserver`.
233
+
234
+ ## Rake Tasks
235
+
236
+ Steep comes with a set of configurable Rake tasks.
237
+
238
+ ```ruby
239
+ # Rakefile
240
+
241
+ require "steep/rake_task"
242
+ Steep::RakeTask.new do |t|
243
+ t.check.severity_level = :error
244
+ t.watch.verbose
245
+ end
246
+
247
+ task default: [:steep]
248
+ ```
249
+
250
+ Use `bundle exec rake -T` to see all available tasks.
251
+
252
+ ## Development
253
+
254
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
255
+
256
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
257
+
258
+ ## Contributing
259
+
260
+ Bug reports and pull requests are welcome on GitHub at https://github.com/soutaro/steep.
data/Rakefile ADDED
@@ -0,0 +1,227 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ if ENV["VSCODE_CWD"]
5
+ require "minitest"
6
+ Minitest.seed = Time.now.to_i
7
+ end
8
+
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << "test"
11
+ t.libs << "lib"
12
+ t.test_files = FileList['test/**/*_test.rb']
13
+ end
14
+
15
+ task :default => :test
16
+
17
+ namespace :test do
18
+ desc "Run output test"
19
+ task :output do
20
+ sh "ruby", "bin/output_test.rb"
21
+ end
22
+ end
23
+
24
+ # Disable the following task enhancement add upstream
25
+ # Rake::Task[:release].enhance do
26
+ # Rake::Task[:"release:note"].invoke
27
+ # Rake::Task[:"release:github"].invoke
28
+ # Rake::Task[:"release:release-prs"].invoke
29
+ # end
30
+
31
+ desc "Generate changelog template from GH pull requests"
32
+ task :changelog do
33
+ major, minor, patch, pre = Steep::VERSION.split(".", 4)
34
+ major = major.to_i
35
+ minor = minor.to_i
36
+ patch = patch.to_i
37
+
38
+ if patch == 0
39
+ milestone = "Steep #{major}.#{minor}"
40
+ else
41
+ milestone = "Steep #{major}.#{minor}.x"
42
+ end
43
+
44
+ puts "🔍 Finding pull requests that is associated to milestone `#{milestone}`..."
45
+
46
+ command = [
47
+ "gh",
48
+ "pr",
49
+ "list",
50
+ "--json",
51
+ "url,title,number",
52
+ "--limit=100",
53
+ "--search" ,
54
+ "milestone:\"#{milestone}\" is:merged sort:updated-desc -label:Released"
55
+ ]
56
+
57
+ require "open3"
58
+ output, status = Open3.capture2(*command)
59
+ raise status.inspect unless status.success?
60
+
61
+ require "json"
62
+ json = JSON.parse(output, symbolize_names: true)
63
+
64
+ unless json.empty?
65
+ puts
66
+ json.each do |line|
67
+ puts "* #{line[:title]} ([##{line[:number]}](#{line[:url]}))"
68
+ end
69
+ else
70
+ puts " (🤑 There is no *unreleased* pull request associated to the milestone.)"
71
+ end
72
+ end
73
+
74
+ namespace :release do
75
+ desc "Ensure release note"
76
+ task :note do
77
+ version = Gem::Version.new(Steep::VERSION)
78
+ major, minor, patch, pre = Steep::VERSION.split(".", 4)
79
+ major = major.to_i
80
+ minor = minor.to_i
81
+ patch = patch.to_i
82
+
83
+ wiki_url = "https://github.com/soutaro/steep/wiki/Release-Note-#{major}.#{minor}"
84
+
85
+ puts "🧐 Checking if Release note page already exists..."
86
+
87
+ unless `curl --silent -o/dev/null --head #{wiki_url} -w '%{http_code}'` == "200"
88
+ pre_flag = version.prerelease? ? " --pre" : ""
89
+ pre_requirement = version.prerelease? ? ", '~> #{major}.#{minor}.#{patch}.#{pre}'" : ""
90
+
91
+ puts "----"
92
+
93
+ puts <<~PREFIX if patch
94
+ **The latest version of Steep #{major}.#{minor} is `#{pre}`.**
95
+
96
+ PREFIX
97
+
98
+ puts <<~TEMPLATE
99
+ Some of the highlights in Steep #{major}.#{minor} are:
100
+
101
+ * New feature 1 (URL)
102
+ * New feature 2 (URL)
103
+ * New feature 3 (URL)
104
+
105
+ You can install it with `$ gem install steep#{pre_flag}` or using Bundler.
106
+
107
+ ```rb
108
+ gem 'steep', require: false#{pre_requirement}
109
+ ```
110
+
111
+ See the [CHANGELOG](https://github.com/soutaro/steep/blob/master/CHANGELOG.md) for the details.
112
+
113
+ ## New feature 1
114
+
115
+ ## New feature 2
116
+
117
+ ## New feature 3
118
+
119
+ ## Diagnostics updates
120
+
121
+ ## Updating Steep
122
+
123
+ TEMPLATE
124
+ puts "----"
125
+ puts
126
+
127
+ puts " ⏩️ Create the release note with the template: #{wiki_url}"
128
+ else
129
+ if patch == 0 || version.prerelease?
130
+ puts " ⏩️ Open the release note and update it at: #{wiki_url}"
131
+ else
132
+ puts " ✅ Release note is ready!"
133
+ end
134
+ end
135
+ puts
136
+ end
137
+
138
+ desc "Create GitHub release automatically"
139
+ task :github do
140
+ version = Gem::Version.new(Steep::VERSION)
141
+ major, minor, patch, *_ = Steep::VERSION.split(".")
142
+ major = major.to_i
143
+ minor = minor.to_i
144
+ patch = patch.to_i
145
+
146
+ puts "✏️ Making a draft release on GitHub..."
147
+
148
+ content = File.read(File.join(__dir__, "CHANGELOG.md"))
149
+ changelog = content.scan(/^## \d.*?(?=^## \d)/m)[0]
150
+ changelog = changelog.sub(/^.*\n^.*\n/, "").rstrip
151
+
152
+ notes = <<NOTES
153
+ [Release note](https://github.com/soutaro/steep/wiki/Release-Note-#{major}.#{minor})
154
+
155
+ #{changelog}
156
+ NOTES
157
+
158
+ command = [
159
+ "gh",
160
+ "release",
161
+ "create",
162
+ "--draft",
163
+ "v#{Steep::VERSION}",
164
+ "--title=#{Steep::VERSION}",
165
+ "--notes=#{notes}"
166
+ ]
167
+
168
+ if version.prerelease?
169
+ command << "--prerelease"
170
+ end
171
+
172
+ require "open3"
173
+ output, status = Open3.capture2(*command)
174
+ if status.success?
175
+ puts " ⏩️ Done! Open #{output.chomp} and publish the release!"
176
+ puts
177
+ end
178
+ end
179
+
180
+ desc "Add `Released` labels to pull requests associated to the version"
181
+ task "release-prs" do
182
+ major, minor, patch, pre = Steep::VERSION.split(".", 4)
183
+ major = major.to_i
184
+ minor = minor.to_i
185
+ patch = patch.to_i
186
+
187
+ if patch == 0
188
+ milestone = "Steep #{major}.#{minor}"
189
+ else
190
+ milestone = "Steep #{major}.#{minor}.x"
191
+ end
192
+
193
+ if pre =~ /dev/
194
+ puts "🔍 Skipping `Released` tags because `dev` release: `#{pre}`"
195
+ next
196
+ end
197
+
198
+ puts "🔍 Finding pull requests that is associated to milestone `#{milestone}`..."
199
+
200
+ command = [
201
+ "gh",
202
+ "pr",
203
+ "list",
204
+ "--json",
205
+ "url,title,number",
206
+ "--search" ,
207
+ "milestone:\"#{milestone}\" is:merged sort:updated-desc -label:Released"
208
+ ]
209
+
210
+ require "open3"
211
+ output, status = Open3.capture2(*command)
212
+ raise status.inspect unless status.success?
213
+
214
+ require "json"
215
+ json = JSON.parse(output, symbolize_names: true)
216
+
217
+ puts " ✅ Found #{json.size} PRs..."
218
+
219
+ json.each do |pr|
220
+ puts "🧐 Updating #{pr[:url]}..."
221
+ output, status = Open3.capture2("gh", "pr", "edit", pr[:number].to_s, "--add-label", "Released")
222
+ raise status.inspect unless status.success?
223
+ puts " ✅ Done!"
224
+ sleep 0.5
225
+ end
226
+ end
227
+ end
data/Steepfile ADDED
@@ -0,0 +1,68 @@
1
+ require "fileutils"
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ FileUtils.mkpath("tmp")
6
+ tmp_rbs_dir = Pathname("tmp/rbs-sig")
7
+
8
+ definition = Bundler::Definition.build(Pathname("Gemfile"), Pathname("Gemfile.lock"), nil)
9
+ rbs_dep = definition.dependencies.find {|dep| dep.name == "rbs" }
10
+ if (source = rbs_dep&.source).is_a?(Bundler::Source::Path)
11
+ unless tmp_rbs_dir.directory?
12
+ FileUtils.ln_s(Pathname.pwd + source.path + "sig", tmp_rbs_dir.to_s, force: true)
13
+ end
14
+ else
15
+ FileUtils.rm_f(tmp_rbs_dir)
16
+ library "rbs"
17
+ end
18
+
19
+ target :app do
20
+ collection_config "rbs_collection.steep.yaml"
21
+
22
+ check "lib"
23
+ ignore "lib/steep/shims"
24
+
25
+ signature "sig"
26
+ ignore_signature "sig/test"
27
+
28
+ implicitly_returns_nil!
29
+
30
+ configure_code_diagnostics(D::Ruby.strict) do |hash|
31
+ end
32
+
33
+ if tmp_rbs_dir.directory?
34
+ signature tmp_rbs_dir.to_s
35
+ else
36
+ library "rbs"
37
+ end
38
+ end
39
+
40
+ target :test do
41
+ collection_config "rbs_collection.steep.yaml"
42
+
43
+ unreferenced!
44
+ implicitly_returns_nil!
45
+
46
+ check "test"
47
+ signature "sig/test"
48
+
49
+ configure_code_diagnostics(D::Ruby.lenient)
50
+
51
+ if tmp_rbs_dir.directory?
52
+ signature tmp_rbs_dir.to_s
53
+ else
54
+ library "rbs"
55
+ end
56
+ end
57
+
58
+ target :bin do
59
+ unreferenced!
60
+ implicitly_returns_nil!
61
+
62
+ collection_config "rbs_collection.steep.yaml"
63
+
64
+ check "bin/generate-diagnostics-docs.rb"
65
+ signature "tmp/rbs-inline/bin"
66
+
67
+ library "rbs"
68
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "steep"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,112 @@
1
+ # rbs_inline: enabled
2
+
3
+ require "rbs"
4
+ require "steep"
5
+
6
+ class RubyDiagnosticsVisitor < RBS::AST::Visitor
7
+ attr_reader :classes #: Hash[String, String]
8
+ attr_reader :templates #: Hash[Symbol, String]
9
+
10
+ def initialize #: void
11
+ @classes = {}
12
+ @templates = {}
13
+ end
14
+
15
+ # @rbs ...
16
+ def visit_declaration_class(node)
17
+ unless node.annotations.find { _1.string == "diagnostics--skip" }
18
+ if node.comment
19
+ name = node.name.to_s
20
+ classes[name] = node.comment.string
21
+ end
22
+ end
23
+
24
+ super
25
+ end
26
+
27
+ # @rbs ...
28
+ def visit_member_method_definition(node)
29
+ if node.annotations.find { _1.string == "diagnostics--template" }
30
+ if node.comment
31
+ templates[node.name] = node.comment.string
32
+ end
33
+ end
34
+ end
35
+
36
+ # @rbs (IO) -> void
37
+ def format_templates(io)
38
+ io.puts "## Configuration Templates"
39
+
40
+ io.puts <<~MD
41
+ Steep provides several templates to configure diagnostics for Ruby code.
42
+ You can use these templates or customize them to suit your needs via `#configure_code_diagnostics` method in `Steepfile`.
43
+
44
+ The following templates are available:
45
+
46
+ MD
47
+
48
+ io.puts "<dl>"
49
+ templates.keys.sort.each do |key|
50
+ body = templates.fetch(key)
51
+
52
+ io.puts "<dt><code>Ruby.#{key}</code></dt>"
53
+ io.puts "<dd>#{body}</dd>"
54
+ end
55
+ io.puts "</dl>"
56
+ io.puts
57
+ end
58
+
59
+ # @rbs (IO) -> void
60
+ def format_class(io)
61
+ classes.keys.sort.each do |key|
62
+ content = classes[key]
63
+
64
+ # io.puts "<h2 id='Ruby::#{key}'>Ruby::#{key}</h2>"
65
+ io.puts "<a name='Ruby::#{key}'></a>"
66
+ io.puts "## Ruby::#{key}"
67
+ io.puts
68
+ io.puts content
69
+ io.puts
70
+
71
+ configs = templates.keys
72
+
73
+ io.puts "### Severity"
74
+ io.puts
75
+ io.puts "| #{configs.map { "#{_1}" }.join(" | ")} |"
76
+ io.puts "| #{configs.map{"-"}.join(" | ")} |"
77
+
78
+ line = configs.map {|config|
79
+ hash = Steep::Diagnostic::Ruby.__send__(config) #: Hash[Class, untyped]
80
+ const =Steep::Diagnostic::Ruby.const_get(key.to_sym)
81
+ "#{hash[const] || "-"}"
82
+ }
83
+ io.puts "| #{line.join(" | ")} |"
84
+ io.puts
85
+ end
86
+ end
87
+
88
+ # @rbs (Pathname) { (instance) -> void } -> void
89
+ def self.visit_file(path, &block)
90
+ STDERR.puts "Reading #{path}..."
91
+ buffer = RBS::Buffer.new(name: path, content: path.read)
92
+ _, _dirs, decls = RBS::Parser.parse_signature(buffer)
93
+
94
+ visitor = new()
95
+ visitor.visit_all(decls)
96
+
97
+ yield visitor
98
+ end
99
+ end
100
+
101
+ diagnostic_dir = Pathname(__dir__ || raise) + "../sig/steep/diagnostic"
102
+ output_dir = Pathname(__dir__ || raise) + "../manual"
103
+
104
+ RubyDiagnosticsVisitor.visit_file(diagnostic_dir + "ruby.rbs") do |visitor|
105
+ STDERR.puts ">> Writing #{output_dir + "ruby-diagnostics.md"}..."
106
+ (output_dir + "ruby-diagnostics.md").open("w") do |io|
107
+ io.puts "# Ruby Code Diagnostics"
108
+ io.puts
109
+ visitor.format_templates(io)
110
+ visitor.format_class(io)
111
+ end
112
+ end