spoom 1.7.8 → 1.7.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aedf28669e780fce835616cbafcd7e45183d0cd474127dc26c1e9d169f3b4920
4
- data.tar.gz: 5ac37ac73623a9158a7ee6d5f60aa93f46d83140fa718129c4fa73fc15ba85f4
3
+ metadata.gz: 82dc04cf069a738410f097150bff4d94af38dc6f7eefca31c368596eaf138da0
4
+ data.tar.gz: ea4046ff175a5c05eb85dc8cc22b6cb88f294d63b378b88a790f6bd4d92bfaff
5
5
  SHA512:
6
- metadata.gz: c309dba40798848147342ffd5c9c4657ce77e8e724d6ece2ceb4851ab8f4c21b432c4684845d1042a81937d52d6cbb47f6b27f3b6440f091e87ddb6c8e8e4077
7
- data.tar.gz: aa5e4e6c8021cc62b4147ef76ff6350c3917838c154d7fdf3ce5ebcf2f54f733bc33bf4caff33ef904a6ef58a8e2b8c436996879982639e7c324110787aa343e
6
+ metadata.gz: a6c72f54e3398d0d599b92d5c266bdea3db2ee78b10151bf9a5644f2679d0778ecef4cfce105bb8dd284947e6c737ff194599c2e72ab7894dd0122466034c553
7
+ data.tar.gz: ed0df1194a93939bbb4117efbc3218d9f18d079cd2dd6b704579ba8e4c78f6f5f8a58b02072dc99bd19870eeaed0bc449ee70ea5c965e152685239b801d97c99
@@ -0,0 +1,33 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Spoom
5
+ module BundlerHelper
6
+ extend T::Sig
7
+
8
+ class << self
9
+ # Generate a gem requirement for the given gem name, using that gem's version in the "real" current bundle.
10
+ #
11
+ # This ensures that any child Spoom::Contexts use predictable gem versions,
12
+ # without having to manually specify them and bump them to stay in sync with Spoom's real Gemfile.
13
+ #
14
+ # Given `"foo"`, returns a string like 'gem "foo", "= 1.2.3"', suitable for inserting into a Gemfile.
15
+ #: (String) -> String
16
+ def gem_requirement_from_real_bundle(gem_name)
17
+ specs = Bundler.load.gems[gem_name]
18
+
19
+ if specs.nil? || specs.empty?
20
+ raise "Did not find gem #{gem_name.inspect} in the current bundle"
21
+ elsif specs.count > 1
22
+ raise <<~MSG
23
+ Found multiple versions of #{gem_name.inspect} in the current bundle:
24
+ #{specs.sort_by(&:version).map { |spec| " - #{spec.name} #{spec.version}" }.join("\n")}
25
+ MSG
26
+ else
27
+ spec = specs.first
28
+ %(gem "#{spec.name}", "= #{spec.version}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,8 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "spoom/bundler_helper"
5
+
4
6
  module Spoom
5
7
  module Cli
6
8
  module Srb
@@ -142,12 +144,14 @@ module Spoom
142
144
  # Now we create a new context to import our modified gem and run tapioca
143
145
  say("Running Tapioca...")
144
146
  tapioca_context = Spoom::Context.mktmp!
145
- tapioca_context.write!("Gemfile", <<~RB)
147
+ tapioca_context.write_gemfile!(<<~GEMFILE)
146
148
  source "https://rubygems.org"
147
149
 
148
- gem "tapioca"
150
+ #{Spoom::BundlerHelper.gem_requirement_from_real_bundle("rbs")}
151
+ #{Spoom::BundlerHelper.gem_requirement_from_real_bundle("tapioca")}
152
+
149
153
  gem "#{spec.name}", path: "#{copy_context.absolute_path}"
150
- RB
154
+ GEMFILE
151
155
  exec(tapioca_context, "bundle install")
152
156
  exec(tapioca_context, "bundle exec tapioca gem #{spec.name} --no-doc --no-loc --no-file-header")
153
157
 
@@ -48,7 +48,7 @@ module Spoom
48
48
  return {} unless file?("Gemfile.lock")
49
49
 
50
50
  parser = Bundler::LockfileParser.new(read_gemfile_lock)
51
- parser.specs.map { |spec| [spec.name, spec] }.to_h
51
+ parser.specs.to_h { |spec| [spec.name, spec] }
52
52
  end
53
53
 
54
54
  # Get `gem` version from the `Gemfile.lock` content
@@ -49,7 +49,7 @@ module Spoom
49
49
 
50
50
  #: (String rb, file: String, ?plugins: Array[Plugins::Base]) -> void
51
51
  def index_ruby(rb, file:, plugins: [])
52
- node = Spoom.parse_ruby(rb, file: file, comments: true)
52
+ node, _ = Spoom.parse_ruby(rb, file: file)
53
53
 
54
54
  # Index definitions
55
55
  model_builder = Model::Builder.new(@model, file)
data/lib/spoom/parse.rb CHANGED
@@ -7,22 +7,21 @@ module Spoom
7
7
  class ParseError < Error; end
8
8
 
9
9
  class << self
10
- #: (String ruby, file: String, ?comments: bool) -> Prism::Node
11
- def parse_ruby(ruby, file:, comments: false)
10
+ #: (String ruby, file: String) -> [Prism::Node, Array[Prism::Comment]]
11
+ def parse_ruby(ruby, file:)
12
12
  result = Prism.parse(ruby)
13
+
13
14
  unless result.success?
14
15
  message = +"Error while parsing #{file}:\n"
15
-
16
16
  result.errors.each do |e|
17
17
  message << "- #{e.message} (at #{e.location.start_line}:#{e.location.start_column})\n"
18
18
  end
19
-
20
19
  raise ParseError, message
21
20
  end
22
21
 
23
- result.attach_comments! if comments
22
+ result.attach_comments!
24
23
 
25
- result.value
24
+ [result.value, result.comments]
26
25
  end
27
26
  end
28
27
  end
data/lib/spoom/poset.rb CHANGED
@@ -119,7 +119,7 @@ module Spoom
119
119
  dot << " \"#{value}\" -> \"#{to}\";\n"
120
120
  end
121
121
  end
122
- if transitive # rubocop:disable Style/Next
122
+ if transitive
123
123
  element.ancestors.each do |ancestor|
124
124
  dot << " \"#{value}\" -> \"#{ancestor}\" [style=dotted];\n"
125
125
  end
data/lib/spoom/rbs.rb CHANGED
@@ -70,6 +70,7 @@ module Spoom
70
70
 
71
71
  class Annotation < Comment; end
72
72
  class Signature < Comment; end
73
+ class TypeAlias < Comment; end
73
74
 
74
75
  module ExtractRBSComments
75
76
  #: (Prism::Node) -> Comments
@@ -13,7 +13,7 @@ module Spoom
13
13
  counters.increment("files")
14
14
 
15
15
  content = File.read(file)
16
- node = Spoom.parse_ruby(content, file: file, comments: true)
16
+ node, _ = Spoom.parse_ruby(content, file: file)
17
17
  visitor = CodeMetricsVisitor.new(counters)
18
18
  visitor.visit(node)
19
19
  end
@@ -14,6 +14,16 @@ module Spoom
14
14
  @max_line_length = max_line_length
15
15
  end
16
16
 
17
+ # @override
18
+ #: (Prism::ProgramNode node) -> void
19
+ def visit_program_node(node)
20
+ # Process all type aliases from the entire file first
21
+ apply_type_aliases(@comments)
22
+
23
+ # Now process the rest of the file with type aliases available
24
+ super
25
+ end
26
+
17
27
  # @override
18
28
  #: (Prism::ClassNode node) -> void
19
29
  def visit_class_node(node)
@@ -274,6 +284,78 @@ module Spoom
274
284
  true
275
285
  end
276
286
  end
287
+
288
+ #: (Array[Prism::Comment]) -> Array[RBS::TypeAlias]
289
+ def collect_type_aliases(comments)
290
+ type_aliases = [] #: Array[RBS::TypeAlias]
291
+
292
+ return type_aliases if comments.empty?
293
+
294
+ continuation_comments = [] #: Array[Prism::Comment]
295
+
296
+ comments.reverse_each do |comment|
297
+ string = comment.slice
298
+
299
+ if string.start_with?("#:")
300
+ string = string.delete_prefix("#:").strip
301
+ location = comment.location
302
+
303
+ if string.start_with?("type ")
304
+ continuation_comments.reverse_each do |continuation_comment|
305
+ string = "#{string}#{continuation_comment.slice.delete_prefix("#|")}"
306
+ location = location.join(continuation_comment.location)
307
+ end
308
+
309
+ type_aliases << Spoom::RBS::TypeAlias.new(string, location)
310
+ end
311
+
312
+ # Clear the continuation comments regardless of whether we found a type alias or not
313
+ continuation_comments.clear
314
+ elsif string.start_with?("#|")
315
+ continuation_comments << comment
316
+ else
317
+ continuation_comments.clear
318
+ end
319
+ end
320
+
321
+ type_aliases
322
+ end
323
+
324
+ #: (Array[Prism::Comment]) -> void
325
+ def apply_type_aliases(comments)
326
+ type_aliases = collect_type_aliases(comments)
327
+
328
+ type_aliases.each do |type_alias|
329
+ indent = " " * type_alias.location.start_column
330
+ insert_pos = adjust_to_line_start(type_alias.location.start_offset)
331
+
332
+ from = insert_pos
333
+ to = adjust_to_line_end(type_alias.location.end_offset)
334
+
335
+ *, decls = ::RBS::Parser.parse_signature(type_alias.string)
336
+
337
+ # We only expect there to be a single type alias declaration
338
+ next unless decls.size == 1 && decls.first.is_a?(::RBS::AST::Declarations::TypeAlias)
339
+
340
+ rbs_type = decls.first
341
+ sorbet_type = RBI::RBS::TypeTranslator.translate(rbs_type.type)
342
+
343
+ alias_name = ::RBS::TypeName.new(
344
+ namespace: rbs_type.name.namespace,
345
+ name: rbs_type.name.name.to_s.gsub(/(?:^|_)([a-z\d]*)/i) do |match|
346
+ match = match.delete_prefix("_")
347
+ !match.empty? ? match[0].upcase.concat(match[1..-1]) : +""
348
+ end,
349
+ )
350
+
351
+ @rewriter << Source::Delete.new(from, to)
352
+ content = "#{indent}#{alias_name} = T.type_alias { #{sorbet_type.to_rbi} }\n"
353
+ @rewriter << Source::Insert.new(insert_pos, content)
354
+ rescue ::RBS::ParsingError, ::RBI::Error
355
+ # Ignore type aliases with errors
356
+ next
357
+ end
358
+ end
277
359
  end
278
360
  end
279
361
  end
@@ -19,8 +19,9 @@ module Spoom
19
19
  ruby_contents.encode("UTF-8")
20
20
  end #: String
21
21
 
22
- node = Spoom.parse_ruby(ruby_contents, file: file, comments: true)
22
+ node, comments = Spoom.parse_ruby(ruby_contents, file: file)
23
23
  @node = node #: Prism::Node
24
+ @comments = comments #: Array[Prism::Comment]
24
25
  @ruby_bytes = ruby_contents.bytes #: Array[Integer]
25
26
  @rewriter = Spoom::Source::Rewriter.new #: Source::Rewriter
26
27
  end
data/lib/spoom/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Spoom
5
- VERSION = "1.7.8"
5
+ VERSION = "1.7.10"
6
6
  end
data/rbi/spoom.rbi CHANGED
@@ -6,8 +6,15 @@
6
6
 
7
7
  module Spoom
8
8
  class << self
9
- sig { params(ruby: ::String, file: ::String, comments: T::Boolean).returns(::Prism::Node) }
10
- def parse_ruby(ruby, file:, comments: T.unsafe(nil)); end
9
+ sig { params(ruby: ::String, file: ::String).returns([::Prism::Node, T::Array[::Prism::Comment]]) }
10
+ def parse_ruby(ruby, file:); end
11
+ end
12
+ end
13
+
14
+ module Spoom::BundlerHelper
15
+ class << self
16
+ sig { params(gem_name: ::String).returns(::String) }
17
+ def gem_requirement_from_real_bundle(gem_name); end
11
18
  end
12
19
  end
13
20
 
@@ -2584,6 +2591,7 @@ module Spoom::RBS::ExtractRBSComments
2584
2591
  end
2585
2592
 
2586
2593
  class Spoom::RBS::Signature < ::Spoom::RBS::Comment; end
2594
+ class Spoom::RBS::TypeAlias < ::Spoom::RBS::Comment; end
2587
2595
  Spoom::SPOOM_PATH = T.let(T.unsafe(nil), String)
2588
2596
  module Spoom::Sorbet; end
2589
2597
  Spoom::Sorbet::BIN_PATH = T.let(T.unsafe(nil), String)
@@ -2893,6 +2901,9 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Trans
2893
2901
  sig { override.params(node: ::Prism::ModuleNode).void }
2894
2902
  def visit_module_node(node); end
2895
2903
 
2904
+ sig { override.params(node: ::Prism::ProgramNode).void }
2905
+ def visit_program_node(node); end
2906
+
2896
2907
  sig { override.params(node: ::Prism::SingletonClassNode).void }
2897
2908
  def visit_singleton_class_node(node); end
2898
2909
 
@@ -2912,6 +2923,12 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Trans
2912
2923
  sig { params(annotations: T::Array[::Spoom::RBS::Annotation], sig: ::RBI::Sig).void }
2913
2924
  def apply_member_annotations(annotations, sig); end
2914
2925
 
2926
+ sig { params(comments: T::Array[::Prism::Comment]).void }
2927
+ def apply_type_aliases(comments); end
2928
+
2929
+ sig { params(comments: T::Array[::Prism::Comment]).returns(T::Array[::Spoom::RBS::TypeAlias]) }
2930
+ def collect_type_aliases(comments); end
2931
+
2915
2932
  sig { params(def_node: ::Prism::DefNode, comments: ::Spoom::RBS::Comments).void }
2916
2933
  def rewrite_def(def_node, comments); end
2917
2934
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spoom
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.8
4
+ version: 1.7.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa
@@ -162,6 +162,7 @@ files:
162
162
  - exe/spoom
163
163
  - lib/spoom.rb
164
164
  - lib/spoom/backtrace_filter/minitest.rb
165
+ - lib/spoom/bundler_helper.rb
165
166
  - lib/spoom/cli.rb
166
167
  - lib/spoom/cli/config.rb
167
168
  - lib/spoom/cli/deadcode.rb