sorbet 0.5.5841
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/bin/srb +150 -0
- data/bin/srb-rbi +237 -0
- data/lib/constant_cache.rb +226 -0
- data/lib/create-config.rb +37 -0
- data/lib/fetch-rbis.rb +129 -0
- data/lib/find-gem-rbis.rb +52 -0
- data/lib/gem-generator-tracepoint.rb +54 -0
- data/lib/gem-generator-tracepoint/tracepoint_serializer.rb +267 -0
- data/lib/gem-generator-tracepoint/tracer.rb +189 -0
- data/lib/gem_loader.rb +1249 -0
- data/lib/gem_loader.rbi +15 -0
- data/lib/hidden-definition-finder.rb +456 -0
- data/lib/real_stdlib.rb +84 -0
- data/lib/require_everything.rb +181 -0
- data/lib/serialize.rb +362 -0
- data/lib/status.rb +21 -0
- data/lib/step_interface.rb +11 -0
- data/lib/suggest-typed.rb +59 -0
- data/lib/t.rb +57 -0
- data/lib/todo-rbi.rb +48 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b72f815f0182861350618922777098663e7c5815
|
4
|
+
data.tar.gz: 6de4663adb5d10acce279bd249fbcb208c60aeaf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fadbd2ab259b46e1481791454260ec447d38eb490766e7ae3c68b9845493e6799059e0605a0ae1c9b3ddcc0d086acabb1b808dde091f740401e6af2398abfc63
|
7
|
+
data.tar.gz: ebc49c3a2518b9de112670bb24989b544753e586bc7dbf74975e8a9b9becbb397318ecdb3afa90c0a006f042f6045ef665afe57c6589646b4760775b3fb377f6
|
data/bin/srb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
#!/usr/bin/env ruby
|
3
|
+
|
4
|
+
# A total hack, but this file is both a valid ruby script and a bash script.
|
5
|
+
=begin 2>/dev/null
|
6
|
+
|
7
|
+
# From here on in, it is all bash until the last line
|
8
|
+
|
9
|
+
help_and_exit() {
|
10
|
+
cat <<EOF
|
11
|
+
A type checker for Ruby
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
srb Same as "srb t"
|
15
|
+
srb (init | initialize) Initializes the \`sorbet\` directory
|
16
|
+
srb rbi [options] Manage the \`sorbet\` directory
|
17
|
+
srb (t | tc | typecheck) [options] Typechecks the code
|
18
|
+
|
19
|
+
Options:
|
20
|
+
-h, --help View help for this subcommand.
|
21
|
+
--version Show version.
|
22
|
+
|
23
|
+
For full help:
|
24
|
+
https://sorbet.org
|
25
|
+
EOF
|
26
|
+
exit 0
|
27
|
+
}
|
28
|
+
|
29
|
+
subcommand=$1
|
30
|
+
shift
|
31
|
+
|
32
|
+
# /path/to/gems/sorbet-0.0.1/bin/srb
|
33
|
+
srb_path="${BASH_SOURCE[0]}"
|
34
|
+
|
35
|
+
compute_md5() {
|
36
|
+
if command -v md5sum > /dev/null; then
|
37
|
+
md5sum "$1"
|
38
|
+
elif command -v md5 > /dev/null; then
|
39
|
+
md5 -r "$1"
|
40
|
+
else
|
41
|
+
# this is quite a bit slower
|
42
|
+
ruby -e 'require "digest"; puts Digest::MD5.hexdigest(File.read(ARGV[0]))' "$1"
|
43
|
+
fi
|
44
|
+
}
|
45
|
+
|
46
|
+
typecheck() {
|
47
|
+
args=("$@")
|
48
|
+
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/sorbet/gem-rbis"
|
49
|
+
|
50
|
+
if [ -z "$SRB_SKIP_GEM_RBIS" ] && [ -f Gemfile.lock ]; then
|
51
|
+
[ -d "$cache_dir" ] || mkdir -p "$cache_dir"
|
52
|
+
cache_hash=$(compute_md5 Gemfile.lock | awk '{ print $1 }')
|
53
|
+
cache_file="${cache_dir}/${cache_hash}"
|
54
|
+
if [ ! -f "$cache_file" ]; then
|
55
|
+
$0 rbi find-gem-rbis > /dev/null
|
56
|
+
fi
|
57
|
+
args+=("@$cache_file")
|
58
|
+
fi
|
59
|
+
|
60
|
+
if [ -n "$SRB_SORBET_EXE" ]; then
|
61
|
+
# shellcheck disable=SC2086
|
62
|
+
"$SRB_SORBET_EXE" "${args[@]}"
|
63
|
+
else
|
64
|
+
# We're using bash string operations here to avoid forking.
|
65
|
+
# Using dirname / basename / etc. would mean ~15ms for each call.
|
66
|
+
|
67
|
+
# /path/to/gems/sorbet-0.0.1
|
68
|
+
without_bin_srb="${srb_path%/bin/srb}"
|
69
|
+
# -0.0.1
|
70
|
+
version_suffix="${without_bin_srb##*/sorbet}"
|
71
|
+
# /path/to/gems
|
72
|
+
gems_path="${without_bin_srb%/sorbet*}"
|
73
|
+
|
74
|
+
# /path/to/gems/sorbet-static-0.0.1-darwin-17/libexec/sorbet
|
75
|
+
# (assumes people only have one platform-depdendent gem installed per version)
|
76
|
+
guess_sorbet=("$gems_path/sorbet-static$version_suffix"*/libexec/sorbet)
|
77
|
+
|
78
|
+
if [[ -f "${guess_sorbet[0]}" ]]; then
|
79
|
+
sorbet="${guess_sorbet[0]}"
|
80
|
+
else
|
81
|
+
# 0.0.1
|
82
|
+
version="${version_suffix#-}"
|
83
|
+
# Resort to locating using the GEM_PATH, this takes ~200ms
|
84
|
+
sorbet="$(VISUAL=echo gem open sorbet-static -v "${version}")/libexec/sorbet"
|
85
|
+
fi
|
86
|
+
|
87
|
+
# if we still don't know the sorbet binary
|
88
|
+
# perhaps this is the java platform
|
89
|
+
if ! [[ -f "${sorbet}" ]]; then
|
90
|
+
if gem env platform | grep -q "java"; then
|
91
|
+
# certain platforms (i.e. java) include multiple binaries
|
92
|
+
# mac.sorbet & linux.sorbet -- since they can't use the host platform (i.e. darwin or linux)
|
93
|
+
# to collect the correct sorbet binary
|
94
|
+
case "$(uname -s)" in
|
95
|
+
Linux*) prefix="linux.";;
|
96
|
+
Darwin*) prefix="mac.";;
|
97
|
+
*) prefix=""
|
98
|
+
esac
|
99
|
+
|
100
|
+
guess_sorbet=("$gems_path/sorbet-static$version_suffix"*/libexec/"${prefix}sorbet")
|
101
|
+
if [[ -f "${guess_sorbet[0]}" ]]; then
|
102
|
+
sorbet="${guess_sorbet[0]}"
|
103
|
+
fi
|
104
|
+
|
105
|
+
fi
|
106
|
+
fi
|
107
|
+
|
108
|
+
# shellcheck disable=SC2086
|
109
|
+
"${sorbet}" "${args[@]}"
|
110
|
+
fi
|
111
|
+
}
|
112
|
+
|
113
|
+
# Dynamically computing the path to the srb-rbi script lets this script work
|
114
|
+
# the same way in local development and when packaged as a gem. Either:
|
115
|
+
#
|
116
|
+
# /path/to/gems/sorbet-0.0.1/bin/srb-rbi
|
117
|
+
# path/to/bin/srb-rbi
|
118
|
+
srb_rbi_path="$srb_path-rbi"
|
119
|
+
|
120
|
+
case $subcommand in
|
121
|
+
"initialize" | "init")
|
122
|
+
"$srb_rbi_path" "init"
|
123
|
+
;;
|
124
|
+
|
125
|
+
"rbi")
|
126
|
+
"$srb_rbi_path" "$@"
|
127
|
+
;;
|
128
|
+
|
129
|
+
"" | "typecheck" | "tc" | "t")
|
130
|
+
if [ ! -d sorbet ] && [ "$#" -eq 0 ]; then
|
131
|
+
echo "No sorbet/ directory found. Maybe you want to run 'srb init'?"
|
132
|
+
echo
|
133
|
+
help_and_exit
|
134
|
+
fi
|
135
|
+
typecheck "$@"
|
136
|
+
;;
|
137
|
+
|
138
|
+
"--version")
|
139
|
+
typecheck --version
|
140
|
+
;;
|
141
|
+
|
142
|
+
*)
|
143
|
+
echo "Unknown subcommand \`$subcommand\`"
|
144
|
+
help_and_exit
|
145
|
+
esac
|
146
|
+
|
147
|
+
exit $?
|
148
|
+
# The closing comment for ruby to be ok with this file
|
149
|
+
=end
|
150
|
+
exec(__FILE__, *ARGV)
|
data/bin/srb-rbi
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
class Sorbet; end
|
4
|
+
module Sorbet::Private; end
|
5
|
+
|
6
|
+
require_relative '../lib/t'
|
7
|
+
require_relative '../lib/step_interface'
|
8
|
+
require_relative '../lib/create-config'
|
9
|
+
require_relative '../lib/gem-generator-tracepoint'
|
10
|
+
require_relative '../lib/hidden-definition-finder'
|
11
|
+
require_relative '../lib/fetch-rbis'
|
12
|
+
require_relative '../lib/find-gem-rbis'
|
13
|
+
require_relative '../lib/suggest-typed'
|
14
|
+
require_relative '../lib/todo-rbi'
|
15
|
+
|
16
|
+
module Sorbet::Private::Main
|
17
|
+
def self.usage
|
18
|
+
"Usage: srb rbi <command>
|
19
|
+
This script gets your current directory ready for using Sorbet by making all sorts of files in ./sorbet/. You should commit them to version control.
|
20
|
+
|
21
|
+
We recommend running it without any options which will execute all the commands in order. If you only need a certain piece, you can pass a command to just run that part.
|
22
|
+
|
23
|
+
You should re-run this script if your program ever stops typechecking due to dynamic code in your project.
|
24
|
+
|
25
|
+
Common Commands:
|
26
|
+
help Print this message
|
27
|
+
<empty> | update Run all initialization commands
|
28
|
+
|
29
|
+
Specific Commands:
|
30
|
+
config Recreate sorbet/config
|
31
|
+
sorbet-typed Download community maintained type definitions for gems
|
32
|
+
gems Generate arity-only type definitions by requiring the gem
|
33
|
+
hidden-definitions Load all your code and generate type definitions for any dynamic code
|
34
|
+
todo Run Sorbet and generate constants which Sorbet errors on
|
35
|
+
suggest-typed Put the highest `typed:` sigil in each of your files
|
36
|
+
find-gem-rbis Find all `rbi/` directories in your gems and list them in `~/.cache/sorbet`
|
37
|
+
"
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.emojify(emoji, msg)
|
41
|
+
if STDOUT.isatty && RUBY_PLATFORM =~ /darwin/
|
42
|
+
"#{emoji} #{msg}"
|
43
|
+
else
|
44
|
+
msg
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.yellow(msg)
|
49
|
+
if STDOUT.isatty
|
50
|
+
"\u001b[0;33m#{msg}\u001b[0m"
|
51
|
+
else
|
52
|
+
msg
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.cyan(msg)
|
57
|
+
if STDOUT.isatty
|
58
|
+
"\u001b[0;36m#{msg}\u001b[0m"
|
59
|
+
else
|
60
|
+
msg
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.init
|
65
|
+
puts "
|
66
|
+
#{emojify("👋", "Hey there!")}
|
67
|
+
|
68
|
+
This script will get this project ready to use with Sorbet by creating a
|
69
|
+
sorbet/ folder for your project. It will contain:
|
70
|
+
|
71
|
+
- a config file
|
72
|
+
- a bunch of 'RBI files'
|
73
|
+
|
74
|
+
RBI stands for 'Ruby Interface'; these files define classes, methods, and
|
75
|
+
constants that exist, but which Sorbet doesn't always know about.
|
76
|
+
|
77
|
+
#{emojify("⚠️ ", "Heads up:")}
|
78
|
+
|
79
|
+
To set up your project, this script will take two potentially destructive
|
80
|
+
actions:
|
81
|
+
|
82
|
+
1. It will #{yellow("require every file in your project")}. Specifically, every script in
|
83
|
+
your project will be run, unless that script checks #{cyan("if __FILE__ == $PROGRAM_NAME")}
|
84
|
+
before running any code, or has the magic comment #{cyan("# typed: ignore")} in it.
|
85
|
+
|
86
|
+
2. It will add a #{yellow("comment to the top of every file")} (like #{cyan("# typed: false")} or
|
87
|
+
#{cyan("# typed: true")}, depending on how many errors were found in that file.)
|
88
|
+
|
89
|
+
"
|
90
|
+
if ENV['SRB_YES'] != nil
|
91
|
+
puts "SRB_YES set, continuing"
|
92
|
+
else
|
93
|
+
STDOUT.write(emojify("❔", "Would you like to continue? [Y/n] "))
|
94
|
+
if STDIN.isatty && STDOUT.isatty
|
95
|
+
begin
|
96
|
+
input = STDIN.gets&.strip
|
97
|
+
if input.nil? || (input != '' && input != 'y' && input != 'Y')
|
98
|
+
puts "\nAborting"
|
99
|
+
Kernel.exit(1)
|
100
|
+
end
|
101
|
+
rescue Interrupt
|
102
|
+
puts "\nAborting"
|
103
|
+
Kernel.exit(1)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
puts "\nNot running interactivly. Set SRB_YES=1 environment variable to proceed"
|
107
|
+
Kernel.exit(1)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Create sorbet/config file
|
112
|
+
make_step(Sorbet::Private::CreateConfig).call
|
113
|
+
|
114
|
+
# Pull in the hand-written RBIs
|
115
|
+
make_step(Sorbet::Private::FetchRBIs).call
|
116
|
+
|
117
|
+
# Generate the RBIs from bundler
|
118
|
+
make_step(Sorbet::Private::GemGeneratorTracepoint).call
|
119
|
+
|
120
|
+
# Find the hidden methods
|
121
|
+
make_step(Sorbet::Private::HiddenMethodFinder).call
|
122
|
+
|
123
|
+
# Run sorbet and make constants to fix errors
|
124
|
+
make_step(Sorbet::Private::TodoRBI).call
|
125
|
+
|
126
|
+
# Run type suggestion once, and then generate the todo.rbi again.
|
127
|
+
#
|
128
|
+
# The first time we suggest typed sigils, a few files may be ignored if they
|
129
|
+
# use Ruby features Sorbet does not support. However, since we ran the
|
130
|
+
# todo.rbi generation before they were ignored, we discovered the constants
|
131
|
+
# that they defined and did not add the constants to todo.rbi file.
|
132
|
+
#
|
133
|
+
# Regenerating after one run of sigil suggestion will put those constants
|
134
|
+
# into the todo.rbi file.
|
135
|
+
Sorbet::Private::SuggestTyped.suggest_typed
|
136
|
+
Sorbet::Private::TodoRBI.main
|
137
|
+
|
138
|
+
# Put some `typed:` sigils
|
139
|
+
make_step(Sorbet::Private::SuggestTyped).call
|
140
|
+
|
141
|
+
puts "
|
142
|
+
#{emojify("✅", "Done!")}
|
143
|
+
|
144
|
+
This project is now set up for use with Sorbet. The sorbet/ folder should exist
|
145
|
+
and look something like this:
|
146
|
+
|
147
|
+
sorbet/
|
148
|
+
#{emojify("├──", " ")}config # Default options to be passed to sorbet on every run
|
149
|
+
#{emojify("└──", " ")}rbi/
|
150
|
+
#{emojify("├──", " ")}sorbet-typed/ # Community written type definition files for your gems
|
151
|
+
#{emojify("├──", " ")}gems/ # Autogenerated type definitions for your gems (from reflection)
|
152
|
+
#{emojify("├──", " ")}hidden-definitions/ # All definitions that exist at runtime, but Sorbet couldn't see statically
|
153
|
+
#{emojify("└──", " ")}todo.rbi # Constants which were still missing, even after the three steps above.
|
154
|
+
|
155
|
+
Please check this whole folder into version control.
|
156
|
+
|
157
|
+
#{emojify("➡️ ", "What's next?")}
|
158
|
+
|
159
|
+
Up to you! First things first, you'll probably want to typecheck your project:
|
160
|
+
|
161
|
+
#{cyan("srb tc")}
|
162
|
+
|
163
|
+
Other than that, it's up to you!
|
164
|
+
We recommend skimming these docs to get a feel for how to use Sorbet:
|
165
|
+
|
166
|
+
- Gradual Type Checking (#{cyan("https://sorbet.org/docs/gradual")})
|
167
|
+
- Enabling Static Checks (#{cyan("https://sorbet.org/docs/static")})
|
168
|
+
- RBI Files (#{cyan("https://sorbet.org/docs/rbi")})
|
169
|
+
|
170
|
+
If instead you want to explore your files locally, here are some things to try:
|
171
|
+
|
172
|
+
- Upgrade a file marked #{cyan("# typed: false")} to #{cyan("# typed: true")}.
|
173
|
+
Then, run #{cyan("srb tc")} and try to fix any errors.
|
174
|
+
- Add signatures to your methods with `sig`.
|
175
|
+
For how, read: #{cyan("https://sorbet.org/docs/sigs")}
|
176
|
+
- Check whether things that show up in the TODO RBI file actually exist in your project.
|
177
|
+
It's possible some of these constants are typos.
|
178
|
+
- Upgrade a file marked #{cyan("# typed: ignore")} to #{cyan("# typed: false")}.
|
179
|
+
Then, run #{cyan("srb rbi hidden-definitions && srb tc")} and try to fix any errors.
|
180
|
+
|
181
|
+
#{emojify("🙌", "Please don't hesitate to give us your feedback!")}
|
182
|
+
"
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.main(argv)
|
186
|
+
if argv.length == 0
|
187
|
+
puts self.usage
|
188
|
+
exit(1)
|
189
|
+
end
|
190
|
+
|
191
|
+
command = case (argv[0])
|
192
|
+
when 'help', '--help'
|
193
|
+
puts self.usage
|
194
|
+
exit(1)
|
195
|
+
when 'init', 'update'
|
196
|
+
self.init
|
197
|
+
return
|
198
|
+
when 'config', Sorbet::Private::CreateConfig.output_file
|
199
|
+
make_step(Sorbet::Private::CreateConfig)
|
200
|
+
when 'sorbet-typed', Sorbet::Private::FetchRBIs.output_file, Sorbet::Private::FetchRBIs::SORBET_RBI_LIST
|
201
|
+
make_step(Sorbet::Private::FetchRBIs)
|
202
|
+
when 'gems', Sorbet::Private::GemGeneratorTracepoint.output_file
|
203
|
+
-> do
|
204
|
+
make_step(Sorbet::Private::GemGeneratorTracepoint).call
|
205
|
+
make_step(Sorbet::Private::SuggestTyped).call
|
206
|
+
end
|
207
|
+
when 'hidden-definitions', Sorbet::Private::HiddenMethodFinder.output_file
|
208
|
+
make_step(Sorbet::Private::HiddenMethodFinder)
|
209
|
+
when 'todo', Sorbet::Private::TodoRBI.output_file
|
210
|
+
make_step(Sorbet::Private::TodoRBI)
|
211
|
+
when 'suggest-typed'
|
212
|
+
make_step(Sorbet::Private::SuggestTyped)
|
213
|
+
|
214
|
+
when 'find-gem-rbis', "#{ENV['HOME']}/.cache/sorbet/gem-rbis", "#{ENV['HOME']}/.cache/sorbet/gem-rbis/"
|
215
|
+
make_step(Sorbet::Private::FindGemRBIs)
|
216
|
+
|
217
|
+
else
|
218
|
+
puts "Unknown command: #{argv[0]}"
|
219
|
+
puts self.usage
|
220
|
+
exit(1)
|
221
|
+
end
|
222
|
+
|
223
|
+
puts "Running command: #{argv[0]}"
|
224
|
+
command.call
|
225
|
+
nil
|
226
|
+
end
|
227
|
+
|
228
|
+
T::Sig::WithoutRuntime.sig {params(step: Sorbet::Private::StepInterface).returns(T.proc.void)}
|
229
|
+
def self.make_step(step)
|
230
|
+
-> do
|
231
|
+
puts "Generating: #{step.output_file}" if step.output_file
|
232
|
+
step.main
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
Sorbet::Private::Main.main(ARGV)
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require_relative './real_stdlib'
|
5
|
+
require_relative './status'
|
6
|
+
|
7
|
+
# This class walks global namespace to find all modules and discover all of their names.
|
8
|
+
# At the time you ask for it it, it takes a "spashot" of the world.
|
9
|
+
# If new modules were defined after an instance of this class was created,
|
10
|
+
# they won't be visible through a previously returned instance.
|
11
|
+
class Sorbet::Private::ConstantLookupCache
|
12
|
+
|
13
|
+
ConstantEntry = Struct.new(:const_name, :found_name, :primary_name, :aliases, :const, :owner)
|
14
|
+
|
15
|
+
# We won't be able to see if someone mankeypatches these.
|
16
|
+
DEPRECATED_CONSTANTS = [
|
17
|
+
'::Bignum',
|
18
|
+
'::Config',
|
19
|
+
'::Data',
|
20
|
+
'::FALSE',
|
21
|
+
'::Fixnum',
|
22
|
+
'::NIL',
|
23
|
+
'::TRUE',
|
24
|
+
'::TimeoutError',
|
25
|
+
'ERB::Compiler::SimpleScanner2',
|
26
|
+
'Net::OpenSSL',
|
27
|
+
'Object::Bignum',
|
28
|
+
'Object::Config',
|
29
|
+
'Object::Data',
|
30
|
+
'Object::FALSE',
|
31
|
+
'Object::Fixnum',
|
32
|
+
'Object::NIL',
|
33
|
+
'Object::TRUE',
|
34
|
+
'Object::TimeoutError',
|
35
|
+
'OpenSSL::Cipher::Cipher',
|
36
|
+
'OpenSSL::Cipher::Digest',
|
37
|
+
'OpenSSL::Digest::Cipher',
|
38
|
+
'OpenSSL::Digest::Digest',
|
39
|
+
'OpenSSL::SSL::SSLContext::METHODS',
|
40
|
+
'Pry::Platform',
|
41
|
+
'Pry::Prompt::MAP',
|
42
|
+
'Sequel::BeforeHookFailed',
|
43
|
+
'Sequel::Database::ResetIdentifierMangling',
|
44
|
+
'Sequel::Error::AdapterNotFound',
|
45
|
+
'Sequel::Error::InvalidOperation',
|
46
|
+
'Sequel::Error::InvalidValue',
|
47
|
+
'Sequel::Error::PoolTimeoutError',
|
48
|
+
'Sequel::Error::Rollback',
|
49
|
+
'Sequel::Model::ANONYMOUS_MODEL_CLASSES',
|
50
|
+
'Sequel::Model::ANONYMOUS_MODEL_CLASSES_MUTEX',
|
51
|
+
'Sequel::UnbindDuplicate',
|
52
|
+
'Sequel::Unbinder',
|
53
|
+
'YARD::Parser::Ruby::Legacy::RipperParser',
|
54
|
+
'Java::ComHeadiusRacc::Cparse::CparseParams::NEVER',
|
55
|
+
'Java::ComHeadiusRacc::Cparse::CparseParams::UNDEF',
|
56
|
+
'Java::ComHeadiusRacc::Cparse::Parser::NEVER',
|
57
|
+
'Java::ComHeadiusRacc::Cparse::Parser::UNDEF',
|
58
|
+
'Java::OrgJruby::RubyBasicObject::NEVER',
|
59
|
+
'Java::OrgJruby::RubyBasicObject::UNDEF',
|
60
|
+
'Java::OrgJruby::RubyClass::NEVER',
|
61
|
+
'Java::OrgJruby::RubyClass::UNDEF',
|
62
|
+
'Java::OrgJruby::RubyModule::NEVER',
|
63
|
+
'Java::OrgJruby::RubyModule::UNDEF',
|
64
|
+
'Java::OrgJruby::RubyObject::NEVER',
|
65
|
+
'Java::OrgJruby::RubyObject::UNDEF',
|
66
|
+
].freeze
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
@all_constants = {}
|
70
|
+
dfs_module(Object, nil, @all_constants, nil)
|
71
|
+
Sorbet::Private::Status.done
|
72
|
+
@consts_by_name = {}
|
73
|
+
@all_constants.each_value do |struct|
|
74
|
+
fill_primary_name(struct)
|
75
|
+
@consts_by_name[struct.primary_name] = struct.const
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def all_module_names
|
80
|
+
ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct|
|
81
|
+
raise "should never happen" if !struct.primary_name
|
82
|
+
struct.primary_name
|
83
|
+
end
|
84
|
+
ret
|
85
|
+
end
|
86
|
+
|
87
|
+
def all_named_modules
|
88
|
+
ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct|
|
89
|
+
raise "should never happen" if !struct.primary_name
|
90
|
+
struct.const
|
91
|
+
end
|
92
|
+
ret
|
93
|
+
end
|
94
|
+
|
95
|
+
def all_module_aliases
|
96
|
+
ret = {}
|
97
|
+
|
98
|
+
@all_constants.map do |_key, struct|
|
99
|
+
next if struct.nil? || !Sorbet::Private::RealStdlib.real_is_a?(struct.const, Module) || struct.aliases.size < 2
|
100
|
+
if struct.owner != nil
|
101
|
+
ret[struct.primary_name] = struct.aliases.reject do |name|
|
102
|
+
# ignore the primary
|
103
|
+
next true if name == struct.primary_name
|
104
|
+
|
105
|
+
prefix, _, _ = name.rpartition('::')
|
106
|
+
|
107
|
+
# an alias that exists at the top-level
|
108
|
+
next false if prefix == ""
|
109
|
+
|
110
|
+
# if the prefix is the same syntactically, then this is a good alias
|
111
|
+
next false if prefix == struct.owner.primary_name
|
112
|
+
|
113
|
+
# skip the alias if the owner is the same
|
114
|
+
other_owner_const = Sorbet::Private::RealStdlib.real_const_get(Object, prefix, false)
|
115
|
+
struct.owner.const == other_owner_const
|
116
|
+
end
|
117
|
+
else
|
118
|
+
# top-level names
|
119
|
+
ret[struct.primary_name] = struct.aliases.reject {|name| name == struct.primary_name }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
ret
|
123
|
+
end
|
124
|
+
|
125
|
+
def class_by_name(name)
|
126
|
+
@consts_by_name[name]
|
127
|
+
end
|
128
|
+
|
129
|
+
def name_by_class(klass)
|
130
|
+
@all_constants[Sorbet::Private::RealStdlib.real_object_id(klass)]&.primary_name
|
131
|
+
end
|
132
|
+
|
133
|
+
private def dfs_module(mod, prefix, ret, owner)
|
134
|
+
raise "error #{prefix}: #{mod} is not a module" if !Sorbet::Private::RealStdlib.real_is_a?(mod, Module)
|
135
|
+
name = Sorbet::Private::RealStdlib.real_name(mod)
|
136
|
+
Sorbet::Private::Status.say("Naming #{name}", print_without_tty: false)
|
137
|
+
return if name == 'RSpec::ExampleGroups' # These are all anonymous classes and will be quadratic in the number of classes to name them. We also know they don't have any hidden definitions
|
138
|
+
begin
|
139
|
+
constants = Sorbet::Private::RealStdlib.real_constants(mod)
|
140
|
+
rescue TypeError
|
141
|
+
puts "Failed to call `constants` on #{prefix}"
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
go_deeper = [] # make this a bfs to prefer shorter names
|
145
|
+
constants.each do |nested|
|
146
|
+
begin
|
147
|
+
next if Sorbet::Private::RealStdlib.real_autoload?(mod, nested) # some constants aren't autoloaded even after require_everything, e.g. StateMachine::Graph
|
148
|
+
|
149
|
+
begin
|
150
|
+
next if DEPRECATED_CONSTANTS.include?("#{prefix}::#{nested}") # avoid stdout spew
|
151
|
+
rescue NameError
|
152
|
+
# If it isn't to_s-able, that is ok, it won't be in our deprecated list
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
156
|
+
begin
|
157
|
+
nested_constant = Sorbet::Private::RealStdlib.real_const_get(mod, nested, false) # rubocop:disable PrisonGuard/NoDynamicConstAccess
|
158
|
+
rescue LoadError => err
|
159
|
+
puts "Got #{err.class} when trying to get nested name #{name}::#{nested}"
|
160
|
+
next
|
161
|
+
rescue NameError, ArgumentError => err
|
162
|
+
puts "Got #{err.class} when trying to get nested name #{name}::#{nested}_"
|
163
|
+
# some stuff fails to load, like
|
164
|
+
# `const_get': uninitialized constant YARD::Parser::Ruby::Legacy::RipperParser (NameError)
|
165
|
+
# Did you mean? YARD::Parser::Ruby::Legacy::RipperParser
|
166
|
+
end
|
167
|
+
|
168
|
+
object_id = Sorbet::Private::RealStdlib.real_object_id(nested_constant)
|
169
|
+
maybe_seen_already = ret[object_id]
|
170
|
+
if Object.eql?(mod) || !prefix
|
171
|
+
nested_name = nested.to_s
|
172
|
+
else
|
173
|
+
nested_name = prefix + "::" + nested.to_s
|
174
|
+
end
|
175
|
+
if maybe_seen_already
|
176
|
+
if nested_name != maybe_seen_already.primary_name
|
177
|
+
maybe_seen_already.aliases << nested_name
|
178
|
+
maybe_seen_already.owner = owner
|
179
|
+
end
|
180
|
+
if maybe_seen_already.primary_name.nil? && Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module)
|
181
|
+
realName = Sorbet::Private::RealStdlib.real_name(nested_constant)
|
182
|
+
maybe_seen_already.primary_name = realName
|
183
|
+
end
|
184
|
+
else
|
185
|
+
entry = ConstantEntry.new(nested, nested_name, nil, [nested_name], nested_constant, owner)
|
186
|
+
ret[object_id] = entry
|
187
|
+
|
188
|
+
if Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module) && Object != nested_constant
|
189
|
+
go_deeper << [entry, nested_constant, nested_name]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Horrible horrible hack to get private constants that are `include`d.
|
197
|
+
# This doesn't recurse so you can't get private constants inside the private
|
198
|
+
# constants, but I really hope you don't need those...
|
199
|
+
Sorbet::Private::RealStdlib.real_ancestors(mod).each do |ancestor|
|
200
|
+
object_id = Sorbet::Private::RealStdlib.real_object_id(ancestor)
|
201
|
+
name = Sorbet::Private::RealStdlib.real_name(ancestor)
|
202
|
+
next unless name
|
203
|
+
next if ret[object_id]
|
204
|
+
prefix, _, const_name = name.rpartition('::')
|
205
|
+
entry = ConstantEntry.new(const_name.to_sym, name, name, [name], ancestor, nil)
|
206
|
+
ret[object_id] = entry
|
207
|
+
end
|
208
|
+
sorted_deeper = go_deeper.sort_by do |entry, nested_constant, nested_name|
|
209
|
+
Sorbet::Private::RealStdlib.real_name(nested_constant)
|
210
|
+
end
|
211
|
+
sorted_deeper.each do |entry, nested_constant, nested_name|
|
212
|
+
dfs_module(nested_constant, nested_name, ret, entry)
|
213
|
+
end
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
|
217
|
+
private def fill_primary_name(struct)
|
218
|
+
return if struct.primary_name
|
219
|
+
if !struct.owner
|
220
|
+
struct.primary_name = struct.found_name
|
221
|
+
else
|
222
|
+
fill_primary_name(struct.owner)
|
223
|
+
struct.primary_name = struct.owner.primary_name + "::" + struct.const_name.to_s
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|