spoom 1.8.1 → 1.8.2

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: 0cbd6c18447795a6c80c58456353edf11b7a591fb745a16b69b85076dbdb283b
4
- data.tar.gz: ffb8f40253e1496b180aa6cee5d4d120567f95698b7e61f2e2992a3cfd978da8
3
+ metadata.gz: 9bf4851363229baee651a7eba14d9f45464db177f094f3b4da791a8b06b82d39
4
+ data.tar.gz: 6aae091a64fb4cb9a55cea3b82307bd413e544c424bd160599eee48cfeefa1e0
5
5
  SHA512:
6
- metadata.gz: fab5f10f8233cec7793b019d427a483f8f0f71dcce16f8661cfb595388fbda72dac8d99fa3f068c4dd6aa24c16c4330bc4111108436c79d294d963b99ae88bee
7
- data.tar.gz: f896ed13b17abd4e483a0a3334d0add82e199cc3c68194932b1c6ae04cfd6f6ce0a48dae475e49cfe86a3e9fe1f61a7b17aeb07f3acb040da9585e15d6e155db
6
+ metadata.gz: ac5bbdffd2be7bb1c8c795eff3e8c332212fd19f033805076af2eee3c11d39ac1d614927dda68997498ed2dc96d929b71c865b7b2f5babbf52283085a807f1c9
7
+ data.tar.gz: 86d82cbeb9b56c7d4046c474e1880fb25c3dd32522a88f39fd3c0bb697b81d17641a4a5a7ac1603bcf319f18d513d429bbaea0ad1653cd29433ad51b0c9da195
@@ -48,8 +48,10 @@ module Spoom
48
48
 
49
49
  node = @node_context.node
50
50
  case node
51
- when Prism::ClassNode, Prism::ModuleNode, Prism::DefNode
51
+ when Prism::ClassNode, Prism::ModuleNode
52
52
  delete_node_and_comments_and_sigs(@node_context)
53
+ when Prism::DefNode
54
+ delete_node_and_comments_and_sigs(modifier_call_context(node) || @node_context)
53
55
  when Prism::ConstantWriteNode, Prism::ConstantOperatorWriteNode,
54
56
  Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
55
57
  Prism::ConstantPathWriteNode, Prism::ConstantPathOperatorWriteNode,
@@ -65,6 +67,47 @@ module Spoom
65
67
 
66
68
  private
67
69
 
70
+ # When a method is defined as the argument of a modifier call (e.g. `private def foo; end` or
71
+ # `private_class_method def self.foo; end`), the `def` node's parent is the call rather than the
72
+ # enclosing class or module body, so its sigs and comments are siblings of the call. Return a
73
+ # context targeting the outermost such call so they are removed together with the method.
74
+ #
75
+ # Modifiers are matched structurally rather than from a fixed list, so user-defined ones are
76
+ # handled too. A call only counts as a modifier when the `def` is its sole argument and it takes
77
+ # no block and is a standalone statement, so we never remove a call that does more than wrap
78
+ # the method (e.g. `register(:thing, def foo; end)` or `FOO = register(def foo; end)`).
79
+ #: (Prism::DefNode def_node) -> NodeContext?
80
+ def modifier_call_context(def_node)
81
+ wrapped = def_node #: Prism::Node
82
+ nesting = @node_context.nesting.dup
83
+ outer_call = nil #: Prism::CallNode?
84
+ outer_nesting = nil #: Array[Prism::Node]?
85
+
86
+ while (ancestor = nesting.pop)
87
+ case ancestor
88
+ when Prism::ArgumentsNode
89
+ # An arguments node sits between a call and its arguments, keep climbing
90
+ next
91
+ when Prism::CallNode
92
+ # Stop unless the call wraps the node as its sole argument and takes no block, otherwise
93
+ # it does more than wrap the method and we must not remove it
94
+ args = ancestor.arguments&.arguments
95
+ break if ancestor.block
96
+ break unless args && args.size == 1 && args.first.equal?(wrapped)
97
+
98
+ wrapped = ancestor
99
+ outer_call = ancestor
100
+ outer_nesting = nesting.dup
101
+ else
102
+ break
103
+ end
104
+ end
105
+ return unless outer_call && outer_nesting
106
+ return unless outer_nesting.last.is_a?(Prism::StatementsNode)
107
+
108
+ NodeContext.new(@old_source, @node_context.comments, outer_call, outer_nesting)
109
+ end
110
+
68
111
  #: (NodeContext context) -> void
69
112
  def delete_constant_assignment(context)
70
113
  case context.node
data/lib/spoom/rbs.rb CHANGED
@@ -68,7 +68,12 @@ module Spoom
68
68
  end
69
69
  end
70
70
 
71
- class Annotation < Comment; end
71
+ class Annotation < Comment
72
+ #: () -> bool
73
+ def abstract?
74
+ @string == "@abstract"
75
+ end
76
+ end
72
77
 
73
78
  class Signature < Comment
74
79
  # Locations of the `#|` continuation comment lines that make up a multiline signature,
@@ -25,6 +25,7 @@ module Spoom
25
25
  end #: Integer?
26
26
 
27
27
  @overloads_strategy = options.overloads_strategy #: Symbol
28
+ @translate_abstract_methods = options.translate_abstract_methods #: bool
28
29
  @type_translator = RBI::RBS::TypeTranslator.new #: RBI::RBS::TypeTranslator
29
30
  end
30
31
 
@@ -141,6 +142,7 @@ module Spoom
141
142
  def rewrite_def(def_node, comments)
142
143
  return if comments.empty?
143
144
  return if comments.signatures.empty?
145
+ return if !@translate_abstract_methods && comments.method_annotations.any?(&:abstract?)
144
146
 
145
147
  signatures = apply_overloads_strategy(
146
148
  comments.signatures,
@@ -47,13 +47,18 @@ module Spoom
47
47
  #: BaseRBIFormat
48
48
  attr_reader :output_format
49
49
 
50
+ #: bool
51
+ attr_reader :translate_abstract_methods
52
+
50
53
  #: (
51
54
  #| ?overloads_strategy: Symbol,
52
55
  #| ?output_format: BaseRBIFormat,
56
+ #| ?translate_abstract_methods: bool,
53
57
  #| ) -> void
54
58
  def initialize(
55
59
  overloads_strategy: :translate_all,
56
- output_format: HumanReadableRBIFormat.default
60
+ output_format: HumanReadableRBIFormat.default,
61
+ translate_abstract_methods: true
57
62
  )
58
63
  unless ALLOWED_OVERLOAD_STRATEGIES.include?(overloads_strategy)
59
64
  raise ArgumentError, "Unknown overloads_strategy: #{overloads_strategy.inspect}. " \
@@ -62,6 +67,7 @@ module Spoom
62
67
 
63
68
  @overloads_strategy = overloads_strategy
64
69
  @output_format = output_format
70
+ @translate_abstract_methods = translate_abstract_methods
65
71
 
66
72
  freeze
67
73
  end
@@ -103,10 +103,12 @@ module Spoom
103
103
  # Otherwise, replace up to the end of the node
104
104
  end_offset = comment_end_offset || node.location.end_offset
105
105
 
106
+ heredoc_body = heredoc_body_within_range(value, end_offset)
107
+
106
108
  replacement = if node.name == :bind
107
109
  "#{rbs_annotation}#{trailing_comment}"
108
110
  else
109
- "#{dedent_value(node, value)} #{rbs_annotation}#{trailing_comment}"
111
+ "#{dedent_value(node, value)} #{rbs_annotation}#{trailing_comment}#{heredoc_body}"
110
112
  end
111
113
 
112
114
  @rewriter << Source::Replace.new(start_offset, end_offset - 1, replacement)
@@ -212,6 +214,53 @@ module Spoom
212
214
  [" #{range.pack("C*")}", end_offset]
213
215
  end
214
216
 
217
+ #: (Prism::Node, Integer) -> String?
218
+ def heredoc_body_within_range(node, replace_end_offset)
219
+ heredoc_end = heredoc_end_offsets(node)
220
+ .select { |offset| offset <= replace_end_offset }
221
+ .max
222
+ return unless heredoc_end
223
+
224
+ opener_line_end = adjust_to_line_end(node.location.end_offset)
225
+ body_bytes = @ruby_bytes[(opener_line_end + 1)...heredoc_end] #: as !nil
226
+ body = body_bytes.pack("C*")
227
+ body.chomp! if @ruby_bytes[replace_end_offset] == LINE_BREAK
228
+ "\n#{body}"
229
+ end
230
+
231
+ #: (Prism::Node) -> Array[Integer]
232
+ def heredoc_end_offsets(node)
233
+ offsets = [] #: Array[Integer]
234
+
235
+ case node
236
+ when Prism::StringNode, Prism::InterpolatedStringNode, Prism::XStringNode, Prism::InterpolatedXStringNode
237
+ opening = node.opening_loc
238
+ closing = node.closing_loc
239
+ if opening && closing && opening.start_line != closing.start_line && opening.slice.start_with?("<<")
240
+ offsets << closing.end_offset
241
+ end
242
+ end
243
+
244
+ node.child_nodes.each do |child|
245
+ next unless child
246
+
247
+ offsets.concat(heredoc_end_offsets(child))
248
+ end
249
+
250
+ offsets
251
+ end
252
+
253
+ #: (Prism::Node) -> bool
254
+ def string_literal?(node)
255
+ case node
256
+ when Prism::StringNode, Prism::InterpolatedStringNode,
257
+ Prism::XStringNode, Prism::InterpolatedXStringNode
258
+ true
259
+ else
260
+ false
261
+ end
262
+ end
263
+
215
264
  #: (Prism::Node, Prism::Node) -> String
216
265
  def dedent_value(assign, value)
217
266
  if value.location.start_line == assign.location.start_line
@@ -243,7 +292,7 @@ module Spoom
243
292
  # ```
244
293
  indent = value.location.start_line - assign.location.start_line
245
294
  lines = value.slice.lines
246
- if lines.size > 1
295
+ if lines.size > 1 && !string_literal?(value)
247
296
  lines[1..]&.each_with_index do |line, i|
248
297
  lines[i + 1] = line.delete_prefix(" " * indent)
249
298
  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.8.1"
5
+ VERSION = "1.8.2"
6
6
  end
data/rbi/spoom.rbi CHANGED
@@ -1519,6 +1519,9 @@ class Spoom::Deadcode::Remover::NodeRemover
1519
1519
  end
1520
1520
  def insert_accessor(node, send_context, was_removed:); end
1521
1521
 
1522
+ sig { params(def_node: ::Prism::DefNode).returns(T.nilable(::Spoom::Deadcode::Remover::NodeContext)) }
1523
+ def modifier_call_context(def_node); end
1524
+
1522
1525
  sig { params(start_char: ::Integer, end_char: ::Integer, replacement: ::String).void }
1523
1526
  def replace_chars(start_char, end_char, replacement); end
1524
1527
 
@@ -2575,7 +2578,11 @@ end
2575
2578
  module Spoom::PrismTypes; end
2576
2579
  Spoom::PrismTypes::AnyScopeNode = T.type_alias { T.any(::Prism::ClassNode, ::Prism::ModuleNode, ::Prism::SingletonClassNode) }
2577
2580
  module Spoom::RBS; end
2578
- class Spoom::RBS::Annotation < ::Spoom::RBS::Comment; end
2581
+
2582
+ class Spoom::RBS::Annotation < ::Spoom::RBS::Comment
2583
+ sig { returns(T::Boolean) }
2584
+ def abstract?; end
2585
+ end
2579
2586
 
2580
2587
  class Spoom::RBS::Comment
2581
2588
  sig { params(string: ::String, location: ::Prism::Location).void }
@@ -3206,10 +3213,11 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::Options
3206
3213
  sig do
3207
3214
  params(
3208
3215
  overloads_strategy: ::Symbol,
3209
- output_format: ::Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::BaseRBIFormat
3216
+ output_format: ::Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::BaseRBIFormat,
3217
+ translate_abstract_methods: T::Boolean
3210
3218
  ).void
3211
3219
  end
3212
- def initialize(overloads_strategy: T.unsafe(nil), output_format: T.unsafe(nil)); end
3220
+ def initialize(overloads_strategy: T.unsafe(nil), output_format: T.unsafe(nil), translate_abstract_methods: T.unsafe(nil)); end
3213
3221
 
3214
3222
  sig { returns(::Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::BaseRBIFormat) }
3215
3223
  def output_format; end
@@ -3217,6 +3225,9 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::Options
3217
3225
  sig { returns(::Symbol) }
3218
3226
  def overloads_strategy; end
3219
3227
 
3228
+ sig { returns(T::Boolean) }
3229
+ def translate_abstract_methods; end
3230
+
3220
3231
  class << self
3221
3232
  sig { returns(::Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs::Options) }
3222
3233
  def default; end
@@ -3262,9 +3273,18 @@ class Spoom::Sorbet::Translate::SorbetAssertionsToRBSComments < ::Spoom::Sorbet:
3262
3273
  sig { params(node: ::Prism::Node).returns(T::Boolean) }
3263
3274
  def has_rbs_annotation?(node); end
3264
3275
 
3276
+ sig { params(node: ::Prism::Node, replace_end_offset: ::Integer).returns(T.nilable(::String)) }
3277
+ def heredoc_body_within_range(node, replace_end_offset); end
3278
+
3279
+ sig { params(node: ::Prism::Node).returns(T::Array[::Integer]) }
3280
+ def heredoc_end_offsets(node); end
3281
+
3265
3282
  sig { params(node: ::Prism::Node).returns(T::Boolean) }
3266
3283
  def maybe_translate_assertion(node); end
3267
3284
 
3285
+ sig { params(node: ::Prism::Node).returns(T::Boolean) }
3286
+ def string_literal?(node); end
3287
+
3268
3288
  sig { params(node: T.nilable(::Prism::Node)).returns(T::Boolean) }
3269
3289
  def t?(node); end
3270
3290
 
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.8.1
4
+ version: 1.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre Terrasa