sorbet 0.4.4250 → 0.4.4253
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/bin/srb +117 -0
- data/bin/srb-rbi +232 -0
- data/lib/constant_cache.rb +193 -0
- data/lib/create-config.rb +32 -0
- data/lib/fetch-rbis.rb +127 -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 +256 -0
- data/lib/gem-generator-tracepoint/tracer.rb +180 -0
- data/lib/gem_loader.rb +579 -0
- data/lib/gem_loader.rbi +15 -0
- data/lib/hidden-definition-finder.rb +441 -0
- data/lib/real_stdlib.rb +79 -0
- data/lib/require_everything.rb +128 -0
- data/lib/serialize.rb +360 -0
- data/lib/status.rb +21 -0
- data/lib/step_interface.rb +11 -0
- data/lib/suggest-typed.rb +41 -0
- data/lib/t.rb +58 -0
- data/lib/todo-rbi.rb +48 -0
- metadata +101 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4a5b41ca48527927442222de4701655747e785db
|
4
|
+
data.tar.gz: 3f85bed09a6177796913319e2d6742a61cbb0b4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f9dd516cea9f1a20448972395c6f19bc27b14fc9c2629631bfc99d587f11d8567663f0b2b41bfefba2c8e06ec26830d54a5e370c04771863ab99300e965582
|
7
|
+
data.tar.gz: d457e3ba50b5a6d75813cfa0ac2803e05ffe322576a48e211b7b35dfb3a3d78a042d5dec01755b8a4f8af2638cd9034afce85eb641c32cf0ba050ba634cc7ed2
|
data/bin/srb
ADDED
@@ -0,0 +1,117 @@
|
|
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
|
+
else
|
39
|
+
md5 -r "$1"
|
40
|
+
fi
|
41
|
+
}
|
42
|
+
|
43
|
+
typecheck() {
|
44
|
+
args=""
|
45
|
+
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/sorbet/gem-rbis"
|
46
|
+
|
47
|
+
if [ -f Gemfile.lock ]; then
|
48
|
+
[ -d "$cache_dir" ] || mkdir "$cache_dir"
|
49
|
+
cache_hash=$(compute_md5 Gemfile.lock | awk '{ print $1 }')
|
50
|
+
cache_file="${cache_dir}/${cache_hash}"
|
51
|
+
if [ ! -f "$cache_file" ]; then
|
52
|
+
$0 rbi find-gem-rbis > /dev/null
|
53
|
+
fi
|
54
|
+
args="@$cache_file "
|
55
|
+
fi
|
56
|
+
args="$args$@"
|
57
|
+
|
58
|
+
if [ -n "$SRB_SORBET_EXE" ]; then
|
59
|
+
# shellcheck disable=SC2086
|
60
|
+
"$SRB_SORBET_EXE" $args
|
61
|
+
else
|
62
|
+
# We're using bash string operations here to avoid forking.
|
63
|
+
# Using dirname / basename / etc. would mean ~15ms for each call.
|
64
|
+
|
65
|
+
# /path/to/gems/sorbet-0.0.1
|
66
|
+
without_bin_srb="${srb_path%/bin/srb}"
|
67
|
+
# -0.0.1
|
68
|
+
version_suffix="${without_bin_srb##*/sorbet}"
|
69
|
+
# /path/to/gems
|
70
|
+
gems_path="${without_bin_srb%/sorbet*}"
|
71
|
+
# /path/to/gems/sorbet-static-0.0.1-darwin-17/libexec/sorbet
|
72
|
+
# (assumes people only have one platform-depdendent gem installed per version)
|
73
|
+
sorbet=("$gems_path/sorbet-static$version_suffix"*/libexec/sorbet)
|
74
|
+
|
75
|
+
# shellcheck disable=SC2086
|
76
|
+
"${sorbet[0]}" $args
|
77
|
+
fi
|
78
|
+
}
|
79
|
+
|
80
|
+
# Dynamically computing the path to the srb-rbi script lets this script work
|
81
|
+
# the same way in local development and when packaged as a gem. Either:
|
82
|
+
#
|
83
|
+
# /path/to/gems/sorbet-0.0.1/bin/srb-rbi
|
84
|
+
# path/to/bin/srb-rbi
|
85
|
+
srb_rbi_path="$srb_path-rbi"
|
86
|
+
|
87
|
+
case $subcommand in
|
88
|
+
"initialize" | "init")
|
89
|
+
"$srb_rbi_path"
|
90
|
+
;;
|
91
|
+
|
92
|
+
"rbi")
|
93
|
+
"$srb_rbi_path" "$@"
|
94
|
+
;;
|
95
|
+
|
96
|
+
"" | "typecheck" | "tc" | "t")
|
97
|
+
if [ ! -d sorbet ] && [ "$#" -eq 0 ]; then
|
98
|
+
echo "No sorbet/ directory found. Maybe you want to run 'srb init'?"
|
99
|
+
echo
|
100
|
+
help_and_exit
|
101
|
+
fi
|
102
|
+
typecheck "$@"
|
103
|
+
;;
|
104
|
+
|
105
|
+
"--version")
|
106
|
+
typecheck --version
|
107
|
+
;;
|
108
|
+
|
109
|
+
*)
|
110
|
+
echo "Unknown subcommand \`$subcommand\`"
|
111
|
+
help_and_exit
|
112
|
+
esac
|
113
|
+
|
114
|
+
exit $?
|
115
|
+
# The closing comment for ruby to be ok with this file
|
116
|
+
=end
|
117
|
+
exec(__FILE__, *ARGV)
|
data/bin/srb-rbi
ADDED
@@ -0,0 +1,232 @@
|
|
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.emojify(emoji, msg)
|
18
|
+
if STDOUT.isatty && RUBY_PLATFORM =~ /darwin/
|
19
|
+
"#{emoji} #{msg}"
|
20
|
+
else
|
21
|
+
msg
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.yellow(msg)
|
26
|
+
if STDOUT.isatty
|
27
|
+
"\u001b[0;33m#{msg}\u001b[0m"
|
28
|
+
else
|
29
|
+
msg
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.cyan(msg)
|
34
|
+
if STDOUT.isatty
|
35
|
+
"\u001b[0;36m#{msg}\u001b[0m"
|
36
|
+
else
|
37
|
+
msg
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.main(argv)
|
42
|
+
command = parse_command(argv)
|
43
|
+
return command.call if command
|
44
|
+
|
45
|
+
puts "
|
46
|
+
#{emojify("👋", "Hey there!")}
|
47
|
+
|
48
|
+
This script will get this project ready to use with Sorbet by creating a
|
49
|
+
sorbet/ folder for your project. It will contain:
|
50
|
+
|
51
|
+
- a config file
|
52
|
+
- a bunch of 'RBI files'
|
53
|
+
|
54
|
+
RBI stands for 'Ruby Interface'; these files define classes, methods, and
|
55
|
+
constants that exist, but which Sorbet doesn't always know about.
|
56
|
+
|
57
|
+
#{emojify("⚠️ ", "Heads up:")}
|
58
|
+
|
59
|
+
To set up your project, this script will take two potentially destructive
|
60
|
+
actions:
|
61
|
+
|
62
|
+
1. It will #{yellow("require every file in your project")}. Specifically, every script in
|
63
|
+
your project will be run, unless that script checks #{cyan("if __FILE__ == $PROGRAM_NAME")}
|
64
|
+
before running any code, or has the magic comment #{cyan("# typed: ignore")} in it.
|
65
|
+
|
66
|
+
2. It will add a #{yellow("comment to the top of every file")} (like #{cyan("# typed: false")} or
|
67
|
+
#{cyan("# typed: true")}, depending on how many errors were found in that file.)
|
68
|
+
|
69
|
+
"
|
70
|
+
if ENV['SRB_YES'] != nil
|
71
|
+
puts "SRB_YES set, continuing"
|
72
|
+
else
|
73
|
+
STDOUT.write(emojify("❔", "Would you like to continue? [Y/n] "))
|
74
|
+
if STDIN.isatty && STDOUT.isatty
|
75
|
+
begin
|
76
|
+
input = Kernel.gets&.strip
|
77
|
+
if input.nil? || (input != '' && input != 'y' && input != 'Y')
|
78
|
+
puts "\nAborting"
|
79
|
+
Kernel.exit(1)
|
80
|
+
end
|
81
|
+
rescue Interrupt
|
82
|
+
puts "\nAborting"
|
83
|
+
Kernel.exit(1)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
puts "\nNot running interactivly. Set SRB_YES=1 environment variable to proceed"
|
87
|
+
Kernel.exit(1)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Create sorbet/config file
|
92
|
+
make_step(Sorbet::Private::CreateConfig).call
|
93
|
+
|
94
|
+
# Pull in the hand-written RBIs
|
95
|
+
make_step(Sorbet::Private::FetchRBIs).call
|
96
|
+
|
97
|
+
# Generate the RBIs from bundler
|
98
|
+
make_step(Sorbet::Private::GemGeneratorTracepoint).call
|
99
|
+
|
100
|
+
# Find the hidden methods
|
101
|
+
make_step(Sorbet::Private::HiddenMethodFinder).call
|
102
|
+
|
103
|
+
# Run sorbet and make constants to fix errors
|
104
|
+
make_step(Sorbet::Private::TodoRBI).call
|
105
|
+
|
106
|
+
# Run type suggestion once, and then generate the todo.rbi again.
|
107
|
+
#
|
108
|
+
# The first time we suggest typed sigils, a few files may be ignored if they
|
109
|
+
# use Ruby features Sorbet does not support. However, since we ran the
|
110
|
+
# todo.rbi generation before they were ignored, we discovered the constants
|
111
|
+
# that they defined and did not add the constants to todo.rbi file.
|
112
|
+
#
|
113
|
+
# Regenerating after one run of sigil suggestion will put those constants
|
114
|
+
# into the todo.rbi file.
|
115
|
+
Sorbet::Private::SuggestTyped.suggest_typed
|
116
|
+
Sorbet::Private::TodoRBI.main
|
117
|
+
|
118
|
+
# Put some `typed:` sigils
|
119
|
+
make_step(Sorbet::Private::SuggestTyped).call
|
120
|
+
|
121
|
+
puts "
|
122
|
+
#{emojify("✅", "Done!")}
|
123
|
+
|
124
|
+
This project is now set up for use with Sorbet. The sorbet/ folder should exist
|
125
|
+
and look something like this:
|
126
|
+
|
127
|
+
sorbet/
|
128
|
+
#{emojify("├──", " ")}config # Default options to passed to sorbet on every run
|
129
|
+
#{emojify("└──", " ")}rbi/
|
130
|
+
#{emojify("├──", " ")}sorbet-typed/ # Community writen type definition files for your gems
|
131
|
+
#{emojify("├──", " ")}gems/ # Autogenerated type definitions for your gems (from reflection)
|
132
|
+
#{emojify("├──", " ")}hidden-definitions/ # All definitions that exist at runtime, but Sorbet couldn't see statically
|
133
|
+
#{emojify("└──", " ")}todo.rbi # Constants which were still missing, even after the three steps above.
|
134
|
+
|
135
|
+
Please check this whole folder into version control.
|
136
|
+
|
137
|
+
#{emojify("➡️ ", "What's next?")}
|
138
|
+
|
139
|
+
Up to you! First things first, you'll probably want to typecheck your project:
|
140
|
+
|
141
|
+
#{cyan("srb tc")}
|
142
|
+
|
143
|
+
Other than that, it's up to you!
|
144
|
+
We recommend skimming these docs to get a feel for how to use Sorbet:
|
145
|
+
|
146
|
+
- Gradual Type Checking (#{cyan("https://sorbet.org/docs/gradual")})
|
147
|
+
- Enabling Static Checks (#{cyan("https://sorbet.org/docs/static")})
|
148
|
+
- RBI Files (#{cyan("https://sorbet.org/docs/rbi")})
|
149
|
+
|
150
|
+
If instead you want to explore your files locally, here are some things to try:
|
151
|
+
|
152
|
+
- Upgrade a file marked # typed: false to # typed: true.
|
153
|
+
Then, run #{cyan("srb tc")} and try to fix any errors.
|
154
|
+
- Add signatures to your methods with `sig`.
|
155
|
+
For how, read: #{cyan("https://sorbet.org/docs/sigs")}
|
156
|
+
- Check whether things that show up in the TODO RBI file actually exist in your project.
|
157
|
+
It's possible some of these constants are typos.
|
158
|
+
- Upgrade a file marked #{cyan("# typed: ignore")} to #{cyan("# typed: false")}.
|
159
|
+
Then, run #{cyan("srb tc")} and try to fix any errors.
|
160
|
+
|
161
|
+
#{emojify("🙌", "Please don't hesitate to give us your feedback!")}
|
162
|
+
"
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.parse_command(argv)
|
166
|
+
return nil if argv.length == 0
|
167
|
+
|
168
|
+
banner = "Usage: srb rbi <command>
|
169
|
+
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.
|
170
|
+
|
171
|
+
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.
|
172
|
+
|
173
|
+
You should re-run this script if your program ever stops typechecking due to dynamic code in your project."
|
174
|
+
commands = "
|
175
|
+
Common Commands:
|
176
|
+
help Print this message
|
177
|
+
<empty> | update Run all initialization commands
|
178
|
+
|
179
|
+
Specific Commands:
|
180
|
+
config Recreate sorbet/config
|
181
|
+
sorbet-typed Download community maintained type definitions for gems
|
182
|
+
gems Generate arity-only type definitions by requiring the gem
|
183
|
+
hidden-definitions Load all your code and generate type definitions for any dynamic code
|
184
|
+
todo Run Sorbet and generate constants which Sorbet errors on
|
185
|
+
suggest-typed Put the highest `typed:` sigil in each of your files
|
186
|
+
find-gem-rbis Find all `rbi/` direcotries in your gems and list them in `~/.cache/sorbet`
|
187
|
+
"
|
188
|
+
|
189
|
+
command = case (argv[0])
|
190
|
+
when 'help', '--help'
|
191
|
+
puts banner
|
192
|
+
puts commands
|
193
|
+
exit(1)
|
194
|
+
when 'config', Sorbet::Private::CreateConfig.output_file
|
195
|
+
make_step(Sorbet::Private::CreateConfig)
|
196
|
+
when 'sorbet-typed', Sorbet::Private::FetchRBIs.output_file, Sorbet::Private::FetchRBIs::SORBET_RBI_LIST
|
197
|
+
make_step(Sorbet::Private::FetchRBIs)
|
198
|
+
when 'gems', Sorbet::Private::GemGeneratorTracepoint.output_file
|
199
|
+
make_step(Sorbet::Private::GemGeneratorTracepoint)
|
200
|
+
when 'hidden-definitions', Sorbet::Private::HiddenMethodFinder.output_file
|
201
|
+
make_step(Sorbet::Private::HiddenMethodFinder)
|
202
|
+
when 'todo', Sorbet::Private::TodoRBI.output_file
|
203
|
+
make_step(Sorbet::Private::TodoRBI)
|
204
|
+
when 'suggest-typed'
|
205
|
+
make_step(Sorbet::Private::SuggestTyped)
|
206
|
+
|
207
|
+
when 'find-gem-rbis', "#{ENV['HOME']}/.cache/sorbet/gem-rbis", "#{ENV['HOME']}/.cache/sorbet/gem-rbis/"
|
208
|
+
make_step(Sorbet::Private::FindGemRBIs)
|
209
|
+
|
210
|
+
when 'update'
|
211
|
+
nil
|
212
|
+
|
213
|
+
else
|
214
|
+
puts "Unknown comand: #{argv[0]}"
|
215
|
+
puts commands
|
216
|
+
exit(1)
|
217
|
+
end
|
218
|
+
|
219
|
+
puts "Running command: #{argv[0]}"
|
220
|
+
command
|
221
|
+
end
|
222
|
+
|
223
|
+
T::Sig::WithoutRuntime.sig {params(step: Sorbet::Private::StepInterface).returns(T.proc.void)}
|
224
|
+
def self.make_step(step)
|
225
|
+
-> do
|
226
|
+
puts "Generating: #{step.output_file}" if step.output_file
|
227
|
+
step.main
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
Sorbet::Private::Main.main(ARGV)
|
@@ -0,0 +1,193 @@
|
|
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
|
+
].freeze
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
@all_constants = {}
|
58
|
+
dfs_module(Object, nil, @all_constants, nil)
|
59
|
+
Sorbet::Private::Status.done
|
60
|
+
@consts_by_name = {}
|
61
|
+
@all_constants.each_value do |struct|
|
62
|
+
fill_primary_name(struct)
|
63
|
+
@consts_by_name[struct.primary_name] = struct.const
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_module_names
|
68
|
+
ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct|
|
69
|
+
raise "should never happen" if !struct.primary_name
|
70
|
+
struct.primary_name
|
71
|
+
end
|
72
|
+
ret
|
73
|
+
end
|
74
|
+
|
75
|
+
def all_named_modules
|
76
|
+
ret = @all_constants.select {|_k, v| Sorbet::Private::RealStdlib.real_is_a?(v.const, Module)}.map do |_key, struct|
|
77
|
+
raise "should never happen" if !struct.primary_name
|
78
|
+
struct.const
|
79
|
+
end
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
|
83
|
+
def all_module_aliases
|
84
|
+
ret = {}
|
85
|
+
|
86
|
+
@all_constants.map do |_key, struct|
|
87
|
+
next if struct.nil? || !Sorbet::Private::RealStdlib.real_is_a?(struct.const, Module) || struct.aliases.size < 2
|
88
|
+
ret[struct.primary_name] = struct.aliases.reject {|name| name == struct.primary_name}
|
89
|
+
end
|
90
|
+
ret
|
91
|
+
end
|
92
|
+
|
93
|
+
def class_by_name(name)
|
94
|
+
@consts_by_name[name]
|
95
|
+
end
|
96
|
+
|
97
|
+
def name_by_class(klass)
|
98
|
+
@all_constants[Sorbet::Private::RealStdlib.real_object_id(klass)]&.primary_name
|
99
|
+
end
|
100
|
+
|
101
|
+
private def dfs_module(mod, prefix, ret, owner)
|
102
|
+
raise "error #{prefix}: #{mod} is not a module" if !Sorbet::Private::RealStdlib.real_is_a?(mod, Module)
|
103
|
+
name = Sorbet::Private::RealStdlib.real_name(mod)
|
104
|
+
Sorbet::Private::Status.say("Naming #{name}", print_without_tty: false)
|
105
|
+
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
|
106
|
+
begin
|
107
|
+
constants = Sorbet::Private::RealStdlib.real_constants(mod)
|
108
|
+
rescue TypeError
|
109
|
+
puts "Failed to call `constants` on #{prefix}"
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
go_deeper = [] # make this a bfs to prefer shorter names
|
113
|
+
constants.each do |nested|
|
114
|
+
begin
|
115
|
+
next if Sorbet::Private::RealStdlib.real_autoload?(mod, nested) # some constants aren't autoloaded even after require_everything, e.g. StateMachine::Graph
|
116
|
+
|
117
|
+
begin
|
118
|
+
next if DEPRECATED_CONSTANTS.include?("#{prefix}::#{nested}") # avoid stdout spew
|
119
|
+
rescue NameError
|
120
|
+
# If it isn't to_s-able, that is ok, it won't be in our deprecated list
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
begin
|
125
|
+
nested_constant = Sorbet::Private::RealStdlib.real_const_get(mod, nested, false) # rubocop:disable PrisonGuard/NoDynamicConstAccess
|
126
|
+
rescue LoadError
|
127
|
+
puts "Failed to load #{name}::#{nested}"
|
128
|
+
next
|
129
|
+
rescue NameError, ArgumentError
|
130
|
+
puts "Failed to load #{name}::#{nested}"
|
131
|
+
# some stuff fails to load, like
|
132
|
+
# `const_get': uninitialized constant YARD::Parser::Ruby::Legacy::RipperParser (NameError)
|
133
|
+
# Did you mean? YARD::Parser::Ruby::Legacy::RipperParser
|
134
|
+
end
|
135
|
+
|
136
|
+
object_id = Sorbet::Private::RealStdlib.real_object_id(nested_constant)
|
137
|
+
maybe_seen_already = ret[object_id]
|
138
|
+
if Object.eql?(mod) || !prefix
|
139
|
+
nested_name = nested.to_s
|
140
|
+
else
|
141
|
+
nested_name = prefix + "::" + nested.to_s
|
142
|
+
end
|
143
|
+
if maybe_seen_already
|
144
|
+
if nested_name != maybe_seen_already.primary_name
|
145
|
+
maybe_seen_already.aliases << nested_name
|
146
|
+
end
|
147
|
+
if maybe_seen_already.primary_name.nil? && Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module)
|
148
|
+
realName = Sorbet::Private::RealStdlib.real_name(nested_constant)
|
149
|
+
maybe_seen_already.primary_name = realName
|
150
|
+
end
|
151
|
+
else
|
152
|
+
entry = ConstantEntry.new(nested, nested_name, nil, [nested_name], nested_constant, owner)
|
153
|
+
ret[object_id] = entry
|
154
|
+
|
155
|
+
if Sorbet::Private::RealStdlib.real_is_a?(nested_constant, Module) && Object != nested_constant
|
156
|
+
go_deeper << [entry, nested_constant, nested_name]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Horrible horrible hack to get private constants that are `include`d.
|
164
|
+
# This doesn't recurse so you can't get private constants inside the private
|
165
|
+
# constants, but I really hope you don't need those...
|
166
|
+
Sorbet::Private::RealStdlib.real_ancestors(mod).each do |ancestor|
|
167
|
+
object_id = Sorbet::Private::RealStdlib.real_object_id(ancestor)
|
168
|
+
name = Sorbet::Private::RealStdlib.real_name(ancestor)
|
169
|
+
next unless name
|
170
|
+
next if ret[object_id]
|
171
|
+
prefix, _, const_name = name.rpartition('::')
|
172
|
+
entry = ConstantEntry.new(const_name.to_sym, name, name, [name], ancestor, nil)
|
173
|
+
ret[object_id] = entry
|
174
|
+
end
|
175
|
+
sorted_deeper = go_deeper.sort_by do |entry, nested_constant, nested_name|
|
176
|
+
Sorbet::Private::RealStdlib.real_name(nested_constant)
|
177
|
+
end
|
178
|
+
sorted_deeper.each do |entry, nested_constant, nested_name|
|
179
|
+
dfs_module(nested_constant, nested_name, ret, entry)
|
180
|
+
end
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
|
184
|
+
private def fill_primary_name(struct)
|
185
|
+
return if struct.primary_name
|
186
|
+
if !struct.owner
|
187
|
+
struct.primary_name = struct.found_name
|
188
|
+
else
|
189
|
+
fill_primary_name(struct.owner)
|
190
|
+
struct.primary_name = struct.owner.primary_name + "::" + struct.const_name.to_s
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|