steep-activesupport-4 1.9.3

Sign up to get free protection for your applications and to get access to all the features.
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