steep-activesupport-4 1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +1032 -0
  5. data/LICENSE +21 -0
  6. data/README.md +260 -0
  7. data/Rakefile +227 -0
  8. data/Steepfile +68 -0
  9. data/bin/console +14 -0
  10. data/bin/generate-diagnostics-docs.rb +112 -0
  11. data/bin/mem_graph.rb +67 -0
  12. data/bin/mem_prof.rb +102 -0
  13. data/bin/output_rebaseline.rb +34 -0
  14. data/bin/output_test.rb +60 -0
  15. data/bin/rbs +20 -0
  16. data/bin/rbs-inline +19 -0
  17. data/bin/setup +9 -0
  18. data/bin/stackprof_test.rb +19 -0
  19. data/bin/steep +19 -0
  20. data/bin/steep-check.rb +251 -0
  21. data/bin/steep-prof +16 -0
  22. data/doc/narrowing.md +195 -0
  23. data/doc/shape.md +194 -0
  24. data/exe/steep +18 -0
  25. data/guides/README.md +5 -0
  26. data/guides/src/gem-rbs-collection/gem-rbs-collection.md +126 -0
  27. data/guides/src/getting-started/getting-started.md +163 -0
  28. data/guides/src/nil-optional/nil-optional.md +195 -0
  29. data/lib/steep/annotation_parser.rb +199 -0
  30. data/lib/steep/ast/annotation/collection.rb +172 -0
  31. data/lib/steep/ast/annotation.rb +137 -0
  32. data/lib/steep/ast/builtin.rb +104 -0
  33. data/lib/steep/ast/ignore.rb +148 -0
  34. data/lib/steep/ast/node/type_application.rb +88 -0
  35. data/lib/steep/ast/node/type_assertion.rb +81 -0
  36. data/lib/steep/ast/types/any.rb +35 -0
  37. data/lib/steep/ast/types/boolean.rb +45 -0
  38. data/lib/steep/ast/types/bot.rb +35 -0
  39. data/lib/steep/ast/types/class.rb +43 -0
  40. data/lib/steep/ast/types/factory.rb +557 -0
  41. data/lib/steep/ast/types/helper.rb +40 -0
  42. data/lib/steep/ast/types/instance.rb +42 -0
  43. data/lib/steep/ast/types/intersection.rb +93 -0
  44. data/lib/steep/ast/types/literal.rb +59 -0
  45. data/lib/steep/ast/types/logic.rb +84 -0
  46. data/lib/steep/ast/types/name.rb +128 -0
  47. data/lib/steep/ast/types/nil.rb +41 -0
  48. data/lib/steep/ast/types/proc.rb +117 -0
  49. data/lib/steep/ast/types/record.rb +79 -0
  50. data/lib/steep/ast/types/self.rb +43 -0
  51. data/lib/steep/ast/types/shared_instance.rb +11 -0
  52. data/lib/steep/ast/types/top.rb +35 -0
  53. data/lib/steep/ast/types/tuple.rb +60 -0
  54. data/lib/steep/ast/types/union.rb +97 -0
  55. data/lib/steep/ast/types/var.rb +65 -0
  56. data/lib/steep/ast/types/void.rb +35 -0
  57. data/lib/steep/cli.rb +401 -0
  58. data/lib/steep/diagnostic/deprecated/else_on_exhaustive_case.rb +20 -0
  59. data/lib/steep/diagnostic/deprecated/unknown_constant_assigned.rb +28 -0
  60. data/lib/steep/diagnostic/helper.rb +18 -0
  61. data/lib/steep/diagnostic/lsp_formatter.rb +78 -0
  62. data/lib/steep/diagnostic/result_printer2.rb +48 -0
  63. data/lib/steep/diagnostic/ruby.rb +1221 -0
  64. data/lib/steep/diagnostic/signature.rb +570 -0
  65. data/lib/steep/drivers/annotations.rb +52 -0
  66. data/lib/steep/drivers/check.rb +339 -0
  67. data/lib/steep/drivers/checkfile.rb +210 -0
  68. data/lib/steep/drivers/diagnostic_printer.rb +105 -0
  69. data/lib/steep/drivers/init.rb +66 -0
  70. data/lib/steep/drivers/langserver.rb +56 -0
  71. data/lib/steep/drivers/print_project.rb +113 -0
  72. data/lib/steep/drivers/stats.rb +203 -0
  73. data/lib/steep/drivers/utils/driver_helper.rb +143 -0
  74. data/lib/steep/drivers/utils/jobs_option.rb +26 -0
  75. data/lib/steep/drivers/vendor.rb +27 -0
  76. data/lib/steep/drivers/watch.rb +194 -0
  77. data/lib/steep/drivers/worker.rb +58 -0
  78. data/lib/steep/equatable.rb +23 -0
  79. data/lib/steep/expectations.rb +228 -0
  80. data/lib/steep/index/rbs_index.rb +350 -0
  81. data/lib/steep/index/signature_symbol_provider.rb +185 -0
  82. data/lib/steep/index/source_index.rb +167 -0
  83. data/lib/steep/interface/block.rb +103 -0
  84. data/lib/steep/interface/builder.rb +843 -0
  85. data/lib/steep/interface/function.rb +1090 -0
  86. data/lib/steep/interface/method_type.rb +330 -0
  87. data/lib/steep/interface/shape.rb +239 -0
  88. data/lib/steep/interface/substitution.rb +159 -0
  89. data/lib/steep/interface/type_param.rb +115 -0
  90. data/lib/steep/located_value.rb +20 -0
  91. data/lib/steep/method_name.rb +42 -0
  92. data/lib/steep/module_helper.rb +24 -0
  93. data/lib/steep/node_helper.rb +273 -0
  94. data/lib/steep/path_helper.rb +30 -0
  95. data/lib/steep/project/dsl.rb +268 -0
  96. data/lib/steep/project/group.rb +31 -0
  97. data/lib/steep/project/options.rb +63 -0
  98. data/lib/steep/project/pattern.rb +59 -0
  99. data/lib/steep/project/target.rb +92 -0
  100. data/lib/steep/project.rb +78 -0
  101. data/lib/steep/rake_task.rb +132 -0
  102. data/lib/steep/range_extension.rb +29 -0
  103. data/lib/steep/server/base_worker.rb +97 -0
  104. data/lib/steep/server/change_buffer.rb +73 -0
  105. data/lib/steep/server/custom_methods.rb +77 -0
  106. data/lib/steep/server/delay_queue.rb +45 -0
  107. data/lib/steep/server/interaction_worker.rb +492 -0
  108. data/lib/steep/server/lsp_formatter.rb +455 -0
  109. data/lib/steep/server/master.rb +912 -0
  110. data/lib/steep/server/target_group_files.rb +205 -0
  111. data/lib/steep/server/type_check_controller.rb +366 -0
  112. data/lib/steep/server/type_check_worker.rb +303 -0
  113. data/lib/steep/server/work_done_progress.rb +64 -0
  114. data/lib/steep/server/worker_process.rb +176 -0
  115. data/lib/steep/services/completion_provider.rb +802 -0
  116. data/lib/steep/services/content_change.rb +61 -0
  117. data/lib/steep/services/file_loader.rb +74 -0
  118. data/lib/steep/services/goto_service.rb +441 -0
  119. data/lib/steep/services/hover_provider/rbs.rb +88 -0
  120. data/lib/steep/services/hover_provider/ruby.rb +221 -0
  121. data/lib/steep/services/hover_provider/singleton_methods.rb +20 -0
  122. data/lib/steep/services/path_assignment.rb +46 -0
  123. data/lib/steep/services/signature_help_provider.rb +202 -0
  124. data/lib/steep/services/signature_service.rb +428 -0
  125. data/lib/steep/services/stats_calculator.rb +68 -0
  126. data/lib/steep/services/type_check_service.rb +394 -0
  127. data/lib/steep/services/type_name_completion.rb +236 -0
  128. data/lib/steep/signature/validator.rb +651 -0
  129. data/lib/steep/source/ignore_ranges.rb +69 -0
  130. data/lib/steep/source.rb +691 -0
  131. data/lib/steep/subtyping/cache.rb +30 -0
  132. data/lib/steep/subtyping/check.rb +1113 -0
  133. data/lib/steep/subtyping/constraints.rb +341 -0
  134. data/lib/steep/subtyping/relation.rb +101 -0
  135. data/lib/steep/subtyping/result.rb +324 -0
  136. data/lib/steep/subtyping/variable_variance.rb +89 -0
  137. data/lib/steep/test.rb +9 -0
  138. data/lib/steep/thread_waiter.rb +43 -0
  139. data/lib/steep/type_construction.rb +5183 -0
  140. data/lib/steep/type_inference/block_params.rb +416 -0
  141. data/lib/steep/type_inference/case_when.rb +303 -0
  142. data/lib/steep/type_inference/constant_env.rb +56 -0
  143. data/lib/steep/type_inference/context.rb +195 -0
  144. data/lib/steep/type_inference/logic_type_interpreter.rb +613 -0
  145. data/lib/steep/type_inference/method_call.rb +193 -0
  146. data/lib/steep/type_inference/method_params.rb +531 -0
  147. data/lib/steep/type_inference/multiple_assignment.rb +194 -0
  148. data/lib/steep/type_inference/send_args.rb +712 -0
  149. data/lib/steep/type_inference/type_env.rb +341 -0
  150. data/lib/steep/type_inference/type_env_builder.rb +138 -0
  151. data/lib/steep/typing.rb +321 -0
  152. data/lib/steep/version.rb +3 -0
  153. data/lib/steep.rb +369 -0
  154. data/manual/annotations.md +181 -0
  155. data/manual/ignore.md +20 -0
  156. data/manual/ruby-diagnostics.md +1879 -0
  157. data/sample/Steepfile +22 -0
  158. data/sample/lib/conference.rb +49 -0
  159. data/sample/lib/length.rb +35 -0
  160. data/sample/sig/conference.rbs +42 -0
  161. data/sample/sig/generics.rbs +15 -0
  162. data/sample/sig/length.rbs +34 -0
  163. data/steep-activesupport-4.gemspec +55 -0
  164. metadata +437 -0
data/bin/mem_graph.rb ADDED
@@ -0,0 +1,67 @@
1
+ class MemGraph
2
+ attr_reader :edges
3
+
4
+ attr_reader :checked
5
+
6
+ attr_reader :generation
7
+
8
+ def initialize(generation)
9
+ @generation = generation
10
+ @edges = []
11
+ @checked = Set.new.compare_by_identity
12
+ @checked << self
13
+ @checked << edges
14
+ @checked << checked
15
+ end
16
+
17
+ IVARS = Object.instance_method(:instance_variables)
18
+ IVGET = Object.instance_method(:instance_variable_get)
19
+
20
+ def traverse(object)
21
+ return if checked.include?(object)
22
+ checked << object
23
+
24
+ case object
25
+ when Array
26
+ object.each do |value|
27
+ insert_edge(object, value)
28
+ traverse(value)
29
+ end
30
+ when Hash
31
+ object.each do |key, value|
32
+ insert_edge(object, key)
33
+ insert_edge(object, value)
34
+
35
+ traverse(key)
36
+ traverse(value)
37
+ end
38
+ else
39
+ IVARS.bind_call(object).each do |name|
40
+ if name.is_a?(Symbol)
41
+ value = IVGET.bind_call(object, name)
42
+ traverse(value)
43
+ insert_edge(object, value)
44
+ else
45
+ STDERR.puts "Unexpected instance variable name: #{name} in #{object.class}"
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ def insert_edge(source, dest)
52
+ case dest
53
+ when Integer, Symbol, nil, true, false, Float
54
+ else
55
+ edges << [
56
+ "#{source.class}(#{source.__id__})",
57
+ "#{dest.class}(#{dest.__id__})",
58
+ ]
59
+ end
60
+ end
61
+
62
+ def dot
63
+ "digraph G {\n" + edges.uniq.map do |source, dest|
64
+ " #{source} -> #{dest};"
65
+ end.join("\n") + "}"
66
+ end
67
+ end
data/bin/mem_prof.rb ADDED
@@ -0,0 +1,102 @@
1
+ require "objspace"
2
+
3
+ class MemProf
4
+ attr_reader :generation
5
+
6
+ def initialize
7
+ end
8
+
9
+ def self.trace(io: STDOUT, &block)
10
+ profiler = MemProf.new
11
+ profiler.start
12
+
13
+ begin
14
+ ret = yield
15
+ rescue
16
+ ObjectSpace.trace_object_allocations_stop
17
+ ObjectSpace.trace_object_allocations_clear
18
+ raise
19
+ end
20
+
21
+ allocated, retained, collected = profiler.stop
22
+
23
+ counts = {}
24
+ collected.each do |id, entry|
25
+ counts[entry] ||= 0
26
+ counts[entry] += 1
27
+ end
28
+
29
+ counts.keys.sort_by {|entry| -counts[entry] }.take(200).each do |entry|
30
+ count = counts.fetch(entry)
31
+ io.puts "#{entry[0]},#{entry[1]},#{entry[2]},#{count}"
32
+ end
33
+
34
+ STDERR.puts "Total allocated: #{allocated.size}"
35
+ STDERR.puts "Total retained: #{retained.size}"
36
+ STDERR.puts "Total collected: #{collected.size}"
37
+
38
+ ret
39
+ end
40
+
41
+ def start
42
+ GC.disable
43
+ 3.times { GC.start }
44
+ GC.start
45
+
46
+ @generation = GC.count
47
+ ObjectSpace.trace_object_allocations_start
48
+ end
49
+
50
+ def stop
51
+ ObjectSpace.trace_object_allocations_stop
52
+
53
+ allocated = objects()
54
+ retained = {}
55
+
56
+ GC.enable
57
+ GC.start
58
+ GC.start
59
+ GC.start
60
+
61
+ ObjectSpace.each_object do |obj|
62
+ next unless ObjectSpace.allocation_generation(obj) == generation
63
+ if o = allocated[obj.__id__]
64
+ retained[obj.__id__] = o
65
+ end
66
+ end
67
+
68
+ # ObjectSpace.trace_object_allocations_clear
69
+
70
+ collected = {}
71
+ allocated.each do |id, state|
72
+ collected[id] = state unless retained.key?(id)
73
+ end
74
+
75
+ [allocated, retained, collected]
76
+ end
77
+
78
+ def objects(hash = {})
79
+ ObjectSpace.each_object do |obj|
80
+ next unless ObjectSpace.allocation_generation(obj) == generation
81
+
82
+ file = ObjectSpace.allocation_sourcefile(obj) || "(no name)"
83
+ line = ObjectSpace.allocation_sourceline(obj)
84
+ klass = object_class(obj)
85
+
86
+ hash[obj.__id__] = [file, line, klass]
87
+ end
88
+
89
+ hash
90
+ end
91
+
92
+ KERNEL_CLASS_METHOD = Kernel.instance_method(:class)
93
+ def object_class(obj)
94
+ klass = obj.class rescue nil
95
+
96
+ unless Class === klass
97
+ # attempt to determine the true Class when .class returns something other than a Class
98
+ klass = KERNEL_CLASS_METHOD.bind_call(obj)
99
+ end
100
+ klass
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ require "pathname"
2
+ require "yaml"
3
+ require "open3"
4
+ require "tempfile"
5
+
6
+ if ARGV.empty?
7
+ test_dirs = (Pathname(__dir__) + "../smoke").children
8
+ else
9
+ test_dirs = ARGV.map {|p| Pathname.pwd + p }
10
+ end
11
+
12
+ failed_tests = []
13
+
14
+ test_dirs.each do |dir|
15
+ puts "Rebaselining #{dir}..."
16
+
17
+ command = %w(steep check --save-expectations=test_expectations.yml)
18
+ puts " command: #{command.join(" ")}"
19
+
20
+ output, status = Open3.capture2(*command, chdir: dir.to_s)
21
+
22
+ unless status.success?
23
+ puts "Error!!! 👺"
24
+ failed_tests << dir.basename
25
+ end
26
+ end
27
+
28
+ if failed_tests.empty?
29
+ puts "Successfully updated output expectations! 🤡"
30
+ else
31
+ puts "Failed to update the following tests! 💀"
32
+ puts " #{failed_tests.join(", ")}"
33
+ exit 1
34
+ end
@@ -0,0 +1,60 @@
1
+ require "pathname"
2
+ require "yaml"
3
+ require "open3"
4
+ require "tempfile"
5
+ require "optparse"
6
+
7
+ OptionParser.new do |opts|
8
+ opts.on("--verbose", "-v") { @verbose = true }
9
+ end.parse!(ARGV)
10
+
11
+ if ARGV.empty?
12
+ test_dirs = (Pathname(__dir__) + "../smoke").children
13
+ else
14
+ test_dirs = ARGV.map {|p| Pathname.pwd + p }
15
+ end
16
+
17
+ failed_tests = []
18
+
19
+ ALLOW_FAILURE = ["diagnostics-ruby-unsat"]
20
+
21
+ test_dirs.each do |dir|
22
+ puts "Running test #{dir}..."
23
+
24
+ unless (dir + "test_expectations.yml").file?
25
+ puts "Skipped ⛹️‍♀️"
26
+ next
27
+ end
28
+
29
+ command = %w(steep check --with-expectations=test_expectations.yml)
30
+ command << "-j2" if ENV["CI"]
31
+ puts " command: #{command.join(" ")}"
32
+
33
+ output, status = Open3.capture2(*command, chdir: dir.to_s)
34
+
35
+ unless status.success?
36
+ unless ALLOW_FAILURE.include?(dir.basename.to_s)
37
+ failed_tests << dir.basename
38
+ puts " Failed! 🤕"
39
+ else
40
+ puts " Failed! 🤕 (ALLOW_FAILURE)"
41
+ end
42
+ else
43
+ puts " Succeed! 👍"
44
+ end
45
+
46
+ if @verbose
47
+ puts " Raw output:"
48
+ output.split(/\n/).each do |line|
49
+ puts " > #{line.chomp}"
50
+ end
51
+ end
52
+ end
53
+
54
+ if failed_tests.empty?
55
+ puts "All tests ok! 👏"
56
+ else
57
+ puts "Errors detected! 🤮"
58
+ puts " #{failed_tests.join(", ")}"
59
+ exit 1
60
+ end
data/bin/rbs ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+
3
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
4
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/../gemfile_steep/Gemfile)
5
+ COLLECTION=$(readlink -f ${BINSTUB_DIR}/../rbs_collection.steep.yaml)
6
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/..)
7
+
8
+ RBS="bundle exec --gemfile=${GEMFILE} rbs --collection=${COLLECTION}"
9
+
10
+ if type "rbenv" > /dev/null 2>&1; then
11
+ RBS="rbenv exec ${RBS}"
12
+ else
13
+ if type "rvm" > /dev/null 2>&1; then
14
+ if [ -e ${ROOT_DIR}/.ruby-version ]; then
15
+ RBS="rvm ${ROOT_DIR} do ${RBS}"
16
+ fi
17
+ fi
18
+ fi
19
+
20
+ exec $RBS $@
data/bin/rbs-inline ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+
3
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
4
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/../gemfile_steep/Gemfile)
5
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/..)
6
+
7
+ RBSINLINE="bundle exec --gemfile=${GEMFILE} rbs-inline"
8
+
9
+ if type "rbenv" > /dev/null 2>&1; then
10
+ RBSINLINE="rbenv exec ${RBSINLINE}"
11
+ else
12
+ if type "rvm" > /dev/null 2>&1; then
13
+ if [ -e ${ROOT_DIR}/.ruby-version ]; then
14
+ RBS="rvm ${ROOT_DIR} do ${RBSINLINE}"
15
+ fi
16
+ fi
17
+ fi
18
+
19
+ exec $RBSINLINE $@
data/bin/setup ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ bundle install --gemfile=gemfile_steep/Gemfile
9
+ bin/rbs collection install
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "stackprof"
4
+
5
+ mode = (ENV["STEEP_STACKPROF_MODE"] || :cpu).to_sym
6
+ out = ENV["STEEP_STACKPROF_OUT"] || "tmp/stackprof-#{mode}-test.dump"
7
+ interval = ENV["STEEP_STACKPROF_INTERVAL"]&.to_i || 1000
8
+
9
+ STDERR.puts "Running profiler: mode => #{mode}, out => #{out}"
10
+ StackProf.run(mode: mode, out: out, raw: true, interval: interval) do
11
+ # 10.times do
12
+ # 1_000.times do
13
+ # Array.new(1_000_000)
14
+ # end
15
+ # sleep 0.1
16
+ # end
17
+
18
+ sleep 5
19
+ end
data/bin/steep ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+
3
+ BINSTUB_DIR=$(cd $(dirname $0); pwd)
4
+ GEMFILE=$(readlink -f ${BINSTUB_DIR}/../gemfile_steep/Gemfile)
5
+ ROOT_DIR=$(readlink -f ${BINSTUB_DIR}/..)
6
+
7
+ STEEP="bundle exec --gemfile=${GEMFILE} steep"
8
+
9
+ if type "rbenv" > /dev/null 2>&1; then
10
+ STEEP="rbenv exec ${STEEP}"
11
+ else
12
+ if type "rvm" > /dev/null 2>&1; then
13
+ if [ -e ${ROOT_DIR}/.ruby-version ]; then
14
+ STEEP="rvm ${ROOT_DIR} do ${STEEP}"
15
+ fi
16
+ fi
17
+ fi
18
+
19
+ exec $STEEP $@
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+
5
+ $LOAD_PATH << Pathname(__dir__) + "../lib"
6
+
7
+ require 'steep'
8
+ require "fileutils"
9
+ require "optparse"
10
+
11
+ puts <<MESSAGE
12
+ This is a single-threaded and one-process type checker for profiling.
13
+ It runs really slow because it's not parallelized, but it's easier to profiling and analyzing the result.
14
+
15
+ $ bundle exec bin/steep-check --target=app
16
+ $ bundle exec bin/steep-check --target=app files...
17
+
18
+ MESSAGE
19
+
20
+ steep_file_path = Pathname("Steepfile")
21
+ target_name = nil #: Symbol?
22
+ profile_mode = :none #: Symbol?
23
+
24
+ OptionParser.new do |opts|
25
+ opts.on("--steepfile=FILE") do |file|
26
+ steep_file_path = Pathname(file)
27
+ end
28
+ opts.on("--target=TARGET") do |target|
29
+ target_name = target.to_sym
30
+ end
31
+ opts.on("--profile=MODE", "vernier, memory, stackprof, none, majo, memory2, dumpall") do |mode|
32
+ profile_mode = mode.to_sym
33
+ end
34
+ end.parse!(ARGV)
35
+
36
+ command_line_args = ARGV.dup
37
+
38
+ class Command
39
+ include Steep
40
+
41
+ attr_reader :project, :target
42
+
43
+ def initialize(steep_file_path:, target_name:)
44
+ @project = Project.new(steepfile_path: steep_file_path).tap do |project|
45
+ Project::DSL.parse(project, steep_file_path.read, filename: steep_file_path.to_s)
46
+ end
47
+
48
+ if target_name
49
+ @target = project.targets.find { _1.name == target_name}
50
+ else
51
+ @target = project.targets[0]
52
+ end
53
+
54
+ unless target
55
+ raise "!! Cannot find a target with #{target_name}: targets=[#{project.targets.map(&:name).join(", ")}]"
56
+ end
57
+ end
58
+
59
+ def load_signatures()
60
+ file_loader = Steep::Services::FileLoader.new(base_dir: project.base_dir)
61
+
62
+ signature_files = {}
63
+ file_loader.each_path_in_patterns(target.signature_pattern) do |path|
64
+ signature_files[path] = path.read
65
+ end
66
+
67
+ target.options.load_collection_lock
68
+
69
+ signature_service = Steep::Project::Target.construct_env_loader(options: target.options, project: project).yield_self do |loader|
70
+ Steep::Services::SignatureService.load_from(loader)
71
+ end
72
+
73
+ env = signature_service.latest_env
74
+
75
+ new_decls = Set[]
76
+
77
+ signature_files.each do |path, content|
78
+ buffer = RBS::Buffer.new(name: path.to_s, content: content)
79
+ buffer, dirs, decls = RBS::Parser.parse_signature(buffer)
80
+ env.add_signature(buffer: buffer, directives: dirs, decls: decls)
81
+ new_decls.merge(decls)
82
+ end
83
+
84
+ env.resolve_type_names(only: new_decls)
85
+ end
86
+
87
+ def type_check_files(command_line_args, env)
88
+ file_loader = Steep::Services::FileLoader.new(base_dir: project.base_dir)
89
+
90
+ source_files = {}
91
+ file_loader.each_path_in_patterns(target.source_pattern, command_line_args) do |path|
92
+ source_files[path] = path.read
93
+ end
94
+
95
+ definition_builder = RBS::DefinitionBuilder.new(env: env)
96
+ factory = AST::Types::Factory.new(builder: definition_builder)
97
+ builder = Interface::Builder.new(factory)
98
+ subtyping = Subtyping::Check.new(builder: builder)
99
+
100
+ typings = {}
101
+
102
+ source_files.each do |path, content|
103
+ source = Source.parse(content, path: path, factory: factory)
104
+
105
+ self_type = AST::Builtin::Object.instance_type
106
+
107
+ annotations = source.annotations(block: source.node, factory: factory, context: nil)
108
+ resolver = RBS::Resolver::ConstantResolver.new(builder: factory.definition_builder)
109
+ const_env = TypeInference::ConstantEnv.new(factory: factory, context: nil, resolver: resolver)
110
+
111
+ type_env = TypeInference::TypeEnvBuilder.new(
112
+ TypeInference::TypeEnvBuilder::Command::ImportGlobalDeclarations.new(factory),
113
+ TypeInference::TypeEnvBuilder::Command::ImportInstanceVariableAnnotations.new(annotations),
114
+ TypeInference::TypeEnvBuilder::Command::ImportConstantAnnotations.new(annotations),
115
+ TypeInference::TypeEnvBuilder::Command::ImportLocalVariableAnnotations.new(annotations)
116
+ ).build(TypeInference::TypeEnv.new(const_env))
117
+
118
+ context = TypeInference::Context.new(
119
+ block_context: nil,
120
+ method_context: nil,
121
+ module_context: TypeInference::Context::ModuleContext.new(
122
+ instance_type: AST::Builtin::Object.instance_type,
123
+ module_type: AST::Builtin::Object.module_type,
124
+ implement_name: nil,
125
+ nesting: nil,
126
+ class_name: AST::Builtin::Object.module_name,
127
+ instance_definition: factory.definition_builder.build_instance(AST::Builtin::Object.module_name),
128
+ module_definition: factory.definition_builder.build_singleton(AST::Builtin::Object.module_name)
129
+ ),
130
+ break_context: nil,
131
+ self_type: self_type,
132
+ type_env: type_env,
133
+ call_context: TypeInference::MethodCall::TopLevelContext.new(),
134
+ variable_context: TypeInference::Context::TypeVariableContext.empty
135
+ )
136
+
137
+ typing = Typing.new(source: source, root_context: context, cursor: nil)
138
+ construction = TypeConstruction.new(checker: subtyping, source: source, annotations: annotations, context: context, typing: typing)
139
+
140
+ construction.synthesize(source.node)
141
+ typings[path] = typing
142
+ end
143
+
144
+ typings
145
+ end
146
+ end
147
+
148
+ command = Command.new(steep_file_path: Pathname.pwd + steep_file_path, target_name: target_name)
149
+
150
+ puts ">> Loading RBS files..."
151
+ env = command.load_signatures()
152
+
153
+ puts ">> Type checking files with #{profile_mode}..."
154
+
155
+ typings = nil
156
+
157
+ GC.start(immediate_sweep: true, immediate_mark: true, full_mark: true)
158
+ # GC.config[:rgengc_allow_major_gc] = false
159
+
160
+ case profile_mode
161
+ when :vernier
162
+ require "vernier"
163
+ out = Pathname.pwd + "tmp/typecheck-#{Process.pid}.vernier.json"
164
+ puts ">> Profiling with vernier: #{out}"
165
+ Vernier.profile(out: out.to_s) do
166
+ typings = command.type_check_files(command_line_args, env)
167
+ end
168
+
169
+ when :memory
170
+ require 'memory_profiler'
171
+ out = Pathname.pwd + "tmp/typecheck-#{Process.pid}.memory.txt"
172
+ puts ">> Profiling with memory_profiler: #{out}"
173
+ classes = nil
174
+ report = MemoryProfiler.report(trace: classes) do
175
+ typings = command.type_check_files(command_line_args, env)
176
+ end
177
+ report.pretty_print(to_file: out, detailed_report: true, scale_bytes: true, retained_strings: false, allocated_strings: false)
178
+
179
+ when :memory2
180
+ require_relative 'mem_prof'
181
+ out = Pathname.pwd + "tmp/typecheck-#{Process.pid}.memory2.csv"
182
+ puts ">> Profiling with mem_prof: #{out}"
183
+ generation = nil
184
+ out.open("w") do |io|
185
+ MemProf.trace(io: io) do
186
+ generation = GC.count
187
+ typings = command.type_check_files(command_line_args, env)
188
+ end
189
+ end
190
+
191
+ require_relative 'mem_graph'
192
+ graph = MemGraph.new(generation)
193
+ ObjectSpace.each_object do |obj|
194
+ if ObjectSpace.allocation_generation(obj) == generation
195
+ graph.traverse(obj)
196
+ end
197
+ end
198
+ (Pathname.pwd + "objects-#{Process.pid}.dot").write(graph.dot)
199
+
200
+ when :stackprof
201
+ require "stackprof"
202
+ out = Pathname.pwd + "tmp/typecheck-#{Process.pid}.stackprof"
203
+ puts ">> Profiling with stackprof: #{out}"
204
+ StackProf.run(out: out, raw: true, interval: 1000) do
205
+ typings = command.type_check_files(command_line_args, env)
206
+ end
207
+
208
+ when :majo
209
+ require "majo"
210
+ out = Pathname.pwd + "tmp/typecheck-#{Process.pid}.majo.csv"
211
+ puts ">> Profiling with majo: #{out}"
212
+
213
+ result = Majo.run do
214
+ typings = command.type_check_files(command_line_args, env)
215
+ end
216
+
217
+ out.open("w") do |io|
218
+ result.report(out: io, formatter: :csv)
219
+ end
220
+
221
+ when :dumpall
222
+ require "objspace"
223
+ out = Pathname.pwd + "tmp/dumpall-#{Process.pid}.json"
224
+ puts ">> Profiling with dumpall: #{out}"
225
+ ObjectSpace.trace_object_allocations_start
226
+ typings = command.type_check_files(command_line_args, env)
227
+ out.open('w+') do |io|
228
+ ObjectSpace.dump_all(output: io)
229
+ end
230
+
231
+ when :none
232
+
233
+ pp rbs_method_types: ObjectSpace.each_object(RBS::MethodType).count
234
+ GC.start(immediate_sweep: true, immediate_mark: true, full_mark: true)
235
+
236
+ pp defs: ObjectSpace.each_object(RBS::AST::Members::MethodDefinition).count
237
+ pp aliases: ObjectSpace.each_object(RBS::AST::Members::Alias).count
238
+ pp attr_reader: ObjectSpace.each_object(RBS::AST::Members::AttrReader).count
239
+ pp attr_writer: ObjectSpace.each_object(RBS::AST::Members::AttrWriter).count
240
+ pp attr_accessor: ObjectSpace.each_object(RBS::AST::Members::AttrAccessor).count
241
+
242
+ Steep.measure("type check", level: :fatal) do
243
+ GC.disable
244
+ typings = command.type_check_files(command_line_args, env)
245
+
246
+ pp steep_method_types: ObjectSpace.each_object(Steep::Interface::MethodType).count, rbs_method_types: ObjectSpace.each_object(RBS::MethodType).count
247
+ pp any: ObjectSpace.each_object(Steep::AST::Types::Any).count, void: ObjectSpace.each_object(Steep::AST::Types::Void).count, self: ObjectSpace.each_object(Steep::AST::Types::Self).count
248
+ end
249
+ end
250
+
251
+ typings.size
data/bin/steep-prof ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "stackprof"
4
+
5
+ mode = (ENV["STEEP_STACKPROF_MODE"] || :cpu).to_sym
6
+ out = ENV["STEEP_STACKPROF_OUT"] || "tmp/stackprof-#{mode}-steep.dump"
7
+ interval = ENV["STEEP_STACKPROF_INTERVAL"]&.to_i || 1000
8
+
9
+ def exit(*)
10
+
11
+ end
12
+
13
+ STDERR.puts "Running profiler: mode=#{mode}, out=#{out}"
14
+ StackProf.run(mode: mode, out: out, raw: true, interval: interval) do
15
+ load File.join(__dir__, "../exe/steep")
16
+ end