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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.gitmodules +0 -0
- data/CHANGELOG.md +1032 -0
- data/LICENSE +21 -0
- data/README.md +260 -0
- data/Rakefile +227 -0
- data/Steepfile +68 -0
- data/bin/console +14 -0
- data/bin/generate-diagnostics-docs.rb +112 -0
- data/bin/mem_graph.rb +67 -0
- data/bin/mem_prof.rb +102 -0
- data/bin/output_rebaseline.rb +34 -0
- data/bin/output_test.rb +60 -0
- data/bin/rbs +20 -0
- data/bin/rbs-inline +19 -0
- data/bin/setup +9 -0
- data/bin/stackprof_test.rb +19 -0
- data/bin/steep +19 -0
- data/bin/steep-check.rb +251 -0
- data/bin/steep-prof +16 -0
- data/doc/narrowing.md +195 -0
- data/doc/shape.md +194 -0
- data/exe/steep +18 -0
- data/guides/README.md +5 -0
- data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
- data/guides/src/getting-started/getting-started.md +163 -0
- data/guides/src/nil-optional/nil-optional.md +195 -0
- data/lib/steep/annotation_parser.rb +199 -0
- data/lib/steep/ast/annotation/collection.rb +172 -0
- data/lib/steep/ast/annotation.rb +137 -0
- data/lib/steep/ast/builtin.rb +104 -0
- data/lib/steep/ast/ignore.rb +148 -0
- data/lib/steep/ast/node/type_application.rb +88 -0
- data/lib/steep/ast/node/type_assertion.rb +81 -0
- data/lib/steep/ast/types/any.rb +35 -0
- data/lib/steep/ast/types/boolean.rb +45 -0
- data/lib/steep/ast/types/bot.rb +35 -0
- data/lib/steep/ast/types/class.rb +43 -0
- data/lib/steep/ast/types/factory.rb +557 -0
- data/lib/steep/ast/types/helper.rb +40 -0
- data/lib/steep/ast/types/instance.rb +42 -0
- data/lib/steep/ast/types/intersection.rb +93 -0
- data/lib/steep/ast/types/literal.rb +59 -0
- data/lib/steep/ast/types/logic.rb +84 -0
- data/lib/steep/ast/types/name.rb +128 -0
- data/lib/steep/ast/types/nil.rb +41 -0
- data/lib/steep/ast/types/proc.rb +117 -0
- data/lib/steep/ast/types/record.rb +79 -0
- data/lib/steep/ast/types/self.rb +43 -0
- data/lib/steep/ast/types/shared_instance.rb +11 -0
- data/lib/steep/ast/types/top.rb +35 -0
- data/lib/steep/ast/types/tuple.rb +60 -0
- data/lib/steep/ast/types/union.rb +97 -0
- data/lib/steep/ast/types/var.rb +65 -0
- data/lib/steep/ast/types/void.rb +35 -0
- data/lib/steep/cli.rb +401 -0
- data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
- data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
- data/lib/steep/diagnostic/helper.rb +18 -0
- data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
- data/lib/steep/diagnostic/result_printer2.rb +48 -0
- data/lib/steep/diagnostic/ruby.rb +1221 -0
- data/lib/steep/diagnostic/signature.rb +570 -0
- data/lib/steep/drivers/annotations.rb +52 -0
- data/lib/steep/drivers/check.rb +339 -0
- data/lib/steep/drivers/checkfile.rb +210 -0
- data/lib/steep/drivers/diagnostic_printer.rb +105 -0
- data/lib/steep/drivers/init.rb +66 -0
- data/lib/steep/drivers/langserver.rb +56 -0
- data/lib/steep/drivers/print_project.rb +113 -0
- data/lib/steep/drivers/stats.rb +203 -0
- data/lib/steep/drivers/utils/driver_helper.rb +143 -0
- data/lib/steep/drivers/utils/jobs_option.rb +26 -0
- data/lib/steep/drivers/vendor.rb +27 -0
- data/lib/steep/drivers/watch.rb +194 -0
- data/lib/steep/drivers/worker.rb +58 -0
- data/lib/steep/equatable.rb +23 -0
- data/lib/steep/expectations.rb +228 -0
- data/lib/steep/index/rbs_index.rb +350 -0
- data/lib/steep/index/signature_symbol_provider.rb +185 -0
- data/lib/steep/index/source_index.rb +167 -0
- data/lib/steep/interface/block.rb +103 -0
- data/lib/steep/interface/builder.rb +843 -0
- data/lib/steep/interface/function.rb +1090 -0
- data/lib/steep/interface/method_type.rb +330 -0
- data/lib/steep/interface/shape.rb +239 -0
- data/lib/steep/interface/substitution.rb +159 -0
- data/lib/steep/interface/type_param.rb +115 -0
- data/lib/steep/located_value.rb +20 -0
- data/lib/steep/method_name.rb +42 -0
- data/lib/steep/module_helper.rb +24 -0
- data/lib/steep/node_helper.rb +273 -0
- data/lib/steep/path_helper.rb +30 -0
- data/lib/steep/project/dsl.rb +268 -0
- data/lib/steep/project/group.rb +31 -0
- data/lib/steep/project/options.rb +63 -0
- data/lib/steep/project/pattern.rb +59 -0
- data/lib/steep/project/target.rb +92 -0
- data/lib/steep/project.rb +78 -0
- data/lib/steep/rake_task.rb +132 -0
- data/lib/steep/range_extension.rb +29 -0
- data/lib/steep/server/base_worker.rb +97 -0
- data/lib/steep/server/change_buffer.rb +73 -0
- data/lib/steep/server/custom_methods.rb +77 -0
- data/lib/steep/server/delay_queue.rb +45 -0
- data/lib/steep/server/interaction_worker.rb +492 -0
- data/lib/steep/server/lsp_formatter.rb +455 -0
- data/lib/steep/server/master.rb +912 -0
- data/lib/steep/server/target_group_files.rb +205 -0
- data/lib/steep/server/type_check_controller.rb +366 -0
- data/lib/steep/server/type_check_worker.rb +303 -0
- data/lib/steep/server/work_done_progress.rb +64 -0
- data/lib/steep/server/worker_process.rb +176 -0
- data/lib/steep/services/completion_provider.rb +802 -0
- data/lib/steep/services/content_change.rb +61 -0
- data/lib/steep/services/file_loader.rb +74 -0
- data/lib/steep/services/goto_service.rb +441 -0
- data/lib/steep/services/hover_provider/rbs.rb +88 -0
- data/lib/steep/services/hover_provider/ruby.rb +221 -0
- data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
- data/lib/steep/services/path_assignment.rb +46 -0
- data/lib/steep/services/signature_help_provider.rb +202 -0
- data/lib/steep/services/signature_service.rb +428 -0
- data/lib/steep/services/stats_calculator.rb +68 -0
- data/lib/steep/services/type_check_service.rb +394 -0
- data/lib/steep/services/type_name_completion.rb +236 -0
- data/lib/steep/signature/validator.rb +651 -0
- data/lib/steep/source/ignore_ranges.rb +69 -0
- data/lib/steep/source.rb +691 -0
- data/lib/steep/subtyping/cache.rb +30 -0
- data/lib/steep/subtyping/check.rb +1113 -0
- data/lib/steep/subtyping/constraints.rb +341 -0
- data/lib/steep/subtyping/relation.rb +101 -0
- data/lib/steep/subtyping/result.rb +324 -0
- data/lib/steep/subtyping/variable_variance.rb +89 -0
- data/lib/steep/test.rb +9 -0
- data/lib/steep/thread_waiter.rb +43 -0
- data/lib/steep/type_construction.rb +5183 -0
- data/lib/steep/type_inference/block_params.rb +416 -0
- data/lib/steep/type_inference/case_when.rb +303 -0
- data/lib/steep/type_inference/constant_env.rb +56 -0
- data/lib/steep/type_inference/context.rb +195 -0
- data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
- data/lib/steep/type_inference/method_call.rb +193 -0
- data/lib/steep/type_inference/method_params.rb +531 -0
- data/lib/steep/type_inference/multiple_assignment.rb +194 -0
- data/lib/steep/type_inference/send_args.rb +712 -0
- data/lib/steep/type_inference/type_env.rb +341 -0
- data/lib/steep/type_inference/type_env_builder.rb +138 -0
- data/lib/steep/typing.rb +321 -0
- data/lib/steep/version.rb +3 -0
- data/lib/steep.rb +369 -0
- data/manual/annotations.md +181 -0
- data/manual/ignore.md +20 -0
- data/manual/ruby-diagnostics.md +1879 -0
- data/sample/Steepfile +22 -0
- data/sample/lib/conference.rb +49 -0
- data/sample/lib/length.rb +35 -0
- data/sample/sig/conference.rbs +42 -0
- data/sample/sig/generics.rbs +15 -0
- data/sample/sig/length.rbs +34 -0
- data/steep-activesupport-4.gemspec +55 -0
- 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
|