rubocop-rbs_inline 1.6.1 → 1.7.0

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 (27) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +70 -0
  4. data/config/default.yml +15 -0
  5. data/lib/rubocop/cop/rbs_inline_cops.rb +6 -0
  6. data/lib/rubocop/cop/style/rbs_inline/class_comment_alignment.rb +66 -0
  7. data/lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb +5 -86
  8. data/lib/rubocop/cop/style/rbs_inline/data_define_with_block.rb +3 -4
  9. data/lib/rubocop/cop/style/rbs_inline/data_struct_helper.rb +97 -0
  10. data/lib/rubocop/cop/style/rbs_inline/missing_class_annotation.rb +85 -0
  11. data/lib/rubocop/cop/style/rbs_inline/missing_data_class_annotation.rb +5 -135
  12. data/lib/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rb +56 -0
  13. data/lib/rubocop/cop/style/rbs_inline/missing_type_annotation.rb +1 -1
  14. data/lib/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rb +58 -0
  15. data/lib/rubocop/cop/style/rbs_inline/struct_new_with_block.rb +54 -0
  16. data/lib/rubocop/rbs_inline/version.rb +1 -1
  17. data/rbs_collection.lock.yaml +5 -1
  18. data/sig/rubocop/cop/style/rbs_inline/class_comment_alignment.rbs +34 -0
  19. data/sig/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rbs +2 -30
  20. data/sig/rubocop/cop/style/rbs_inline/data_define_with_block.rbs +1 -1
  21. data/sig/rubocop/cop/style/rbs_inline/data_struct_helper.rbs +55 -0
  22. data/sig/rubocop/cop/style/rbs_inline/missing_class_annotation.rbs +41 -0
  23. data/sig/rubocop/cop/style/rbs_inline/missing_data_class_annotation.rbs +2 -46
  24. data/sig/rubocop/cop/style/rbs_inline/missing_struct_class_annotation.rbs +46 -0
  25. data/sig/rubocop/cop/style/rbs_inline/struct_class_comment_alignment.rbs +48 -0
  26. data/sig/rubocop/cop/style/rbs_inline/struct_new_with_block.rbs +40 -0
  27. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd596a937ef29c9dd1053c9b874725aebde2e9fab387053243268810bb0a8103
4
- data.tar.gz: '03865a85b9ef1783eb85816c386aee96370e48e7169994ea5cc96c1a323aacd9'
3
+ metadata.gz: 5f1bf0f9d17e73042a1c33ae69ffe0cf079bedb1423119b36a524d085cfe6f6a
4
+ data.tar.gz: '0797210c998bb891bcd07b68f3d4cc9e779cf775ea051e6529c8b052ed58642a'
5
5
  SHA512:
6
- metadata.gz: a5c053242db495271ee4751bcba23bc67d876d8d668e0984634d1154452a87476ff82330ea9010c48cd0f285918124d2dc42145bb750a3a8d0f25368e8ee8e22
7
- data.tar.gz: 0e78d76dc58a9ba77439f65013cf1aa46492ee84337a43c6630e81d18c02d5fba23b0fab88c42f7a73d35d3c3bf02c4cfc9bee50f9337fb4d0d2a5189ddfdbf1
6
+ metadata.gz: bb484322f4605db7c86dddf047de1ad1dbe477faf11b7d736edaa544132b87c1b7c0481cd13a6154662a0e318daeaf4880102882288cf02b84ba88eb8d3372f5
7
+ data.tar.gz: 141d659c28c9c9b2bef362b7f5b57bccf68aaec436b553e19785d97d162a0293c94d0d79ad7d29b7300f0b2583cce353c3152503ec965ab521eeff6fe3bb1127
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.0 (2026-07-20)
4
+
5
+ ### New Features
6
+
7
+ - **Style/RbsInline/MissingStructClassAnnotation**: New cop that checks each attribute passed to `Struct.new` has a trailing `#:` inline type annotation. Supports autocorrect.
8
+ - **Style/RbsInline/StructClassCommentAlignment**: New cop that checks `#:` inline type annotations in a multiline `Struct.new` call are aligned. Supports autocorrect.
9
+ - **Style/RbsInline/StructNewWithBlock**: New cop that checks for `Struct.new` calls with a block, which RBS::Inline does not parse.
10
+
3
11
  ## 1.6.1 (2026-07-10)
4
12
 
5
13
  ### Bug Fixes
data/README.md CHANGED
@@ -198,6 +198,32 @@ MethodEntry = Data.define(
198
198
  )
199
199
  ```
200
200
 
201
+ ### Style/RbsInline/MissingStructClassAnnotation
202
+
203
+ Checks that each attribute passed to `Struct.new` has a trailing `#:` inline type annotation on the same line.
204
+
205
+ For folded `Struct.new` calls (where multiple attributes share a line), the cop will suggest rewriting as a multiline call so each attribute can be annotated individually. A leading string argument (the struct name) and the `keyword_init:` keyword argument are not attributes and are ignored.
206
+
207
+ Supports autocorrect.
208
+
209
+ **Examples:**
210
+ ```ruby
211
+ # bad
212
+ Point = Struct.new(:x, :y)
213
+
214
+ # bad - missing annotation for :y
215
+ Point = Struct.new(
216
+ :x, #: Integer
217
+ :y
218
+ )
219
+
220
+ # good
221
+ Point = Struct.new(
222
+ :x, #: Integer
223
+ :y #: Integer
224
+ )
225
+ ```
226
+
201
227
  ### Style/RbsInline/MissingTypeAnnotation
202
228
 
203
229
  Enforces that method definitions and `attr_*` declarations have RBS inline type annotations.
@@ -458,6 +484,50 @@ class Foo
458
484
  end
459
485
  ```
460
486
 
487
+ ### Style/RbsInline/StructClassCommentAlignment
488
+
489
+ Checks that `#:` inline type annotations in a multiline `Struct.new` call are aligned to the same column. The expected column is determined by the longest attribute name (plus its trailing comma). Folded `Struct.new` calls (where multiple attributes share a line) are excluded.
490
+
491
+ Supports autocorrect.
492
+
493
+ **Examples:**
494
+ ```ruby
495
+ # bad
496
+ Point = Struct.new(
497
+ :x, #: Integer
498
+ :long_attr #: Integer
499
+ )
500
+
501
+ # good
502
+ Point = Struct.new(
503
+ :x, #: Integer
504
+ :long_attr #: Integer
505
+ )
506
+ ```
507
+
508
+ ### Style/RbsInline/StructNewWithBlock
509
+
510
+ Checks for `Struct.new` calls with a block. RBS::Inline does not parse block contents, so any methods defined inside the block will not be recognized for type checking. Instead, call `Struct.new` without a block and reopen the class separately to add methods.
511
+
512
+ **Examples:**
513
+ ```ruby
514
+ # bad
515
+ User = Struct.new(:name, :role) do
516
+ def admin?
517
+ role == :admin
518
+ end
519
+ end
520
+
521
+ # good
522
+ User = Struct.new(:name, :role)
523
+
524
+ class User
525
+ def admin? #: bool
526
+ role == :admin
527
+ end
528
+ end
529
+ ```
530
+
461
531
  ### Style/RbsInline/UnmatchedAnnotations
462
532
 
463
533
  Verifies that annotation parameters match the actual method parameters.
data/config/default.yml CHANGED
@@ -46,6 +46,11 @@ Style/RbsInline/MissingDataClassAnnotation:
46
46
  Enabled: true
47
47
  VersionAdded: '1.5.0'
48
48
 
49
+ Style/RbsInline/MissingStructClassAnnotation:
50
+ Description: "Checks that `Struct.new` attributes have inline type annotations."
51
+ Enabled: true
52
+ VersionAdded: '1.7.0'
53
+
49
54
  Style/RbsInline/MissingTypeAnnotation:
50
55
  Description: "Checks that method definitions and attr_* declarations have RBS inline type annotations."
51
56
  Enabled: true
@@ -94,6 +99,16 @@ Style/RbsInline/RequireRbsInlineComment:
94
99
  - always
95
100
  - never
96
101
 
102
+ Style/RbsInline/StructClassCommentAlignment:
103
+ Description: "Checks that `#:` inline type annotations in `Struct.new` blocks are aligned."
104
+ Enabled: true
105
+ VersionAdded: '1.7.0'
106
+
107
+ Style/RbsInline/StructNewWithBlock:
108
+ Description: "Checks for `Struct.new` calls with a block, which RBS::Inline does not parse."
109
+ Enabled: true
110
+ VersionAdded: '1.7.0'
111
+
97
112
  Style/RbsInline/UnmatchedAnnotations:
98
113
  Description: "Checks the rbs-inline annotation comment is surely matched."
99
114
  Enabled: true
@@ -3,6 +3,9 @@
3
3
  require_relative "style/rbs_inline/ast_utils"
4
4
  require_relative "style/rbs_inline/source_code_helper"
5
5
  require_relative "style/rbs_inline/comment_parser"
6
+ require_relative "style/rbs_inline/data_struct_helper"
7
+ require_relative "style/rbs_inline/missing_class_annotation"
8
+ require_relative "style/rbs_inline/class_comment_alignment"
6
9
  require_relative "style/rbs_inline/data_class_comment_alignment"
7
10
  require_relative "style/rbs_inline/data_define_with_block"
8
11
  require_relative "style/rbs_inline/embedded_rbs_spacing"
@@ -17,6 +20,9 @@ require_relative "style/rbs_inline/redundant_annotation_with_skip"
17
20
  require_relative "style/rbs_inline/redundant_instance_variable_annotation"
18
21
  require_relative "style/rbs_inline/redundant_type_annotation"
19
22
  require_relative "style/rbs_inline/require_rbs_inline_comment"
23
+ require_relative "style/rbs_inline/struct_class_comment_alignment"
24
+ require_relative "style/rbs_inline/struct_new_with_block"
25
+ require_relative "style/rbs_inline/missing_struct_class_annotation"
20
26
  require_relative "style/rbs_inline/unmatched_annotations"
21
27
  require_relative "style/rbs_inline/untyped_instance_variable"
22
28
  require_relative "style/rbs_inline/variable_comment_spacing"
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ module RbsInline
7
+ # Shared behavior for cops that ensure `#:` inline type annotations on
8
+ # struct-like class attributes (`Data.define` / `Struct.new`) are aligned.
9
+ #
10
+ # The including cop matches the definition node (e.g. via `struct_like_class?`)
11
+ # and calls {#check_comment_alignment}.
12
+ #
13
+ # @rbs module-self RuboCop::Cop::Base
14
+ module ClassCommentAlignment
15
+ include DataStructHelper
16
+
17
+ # @rbs node: RuboCop::AST::SendNode
18
+ def check_comment_alignment(node) #: void
19
+ return if folded?(node)
20
+
21
+ check_annotation_alignment(node)
22
+ end
23
+
24
+ private
25
+
26
+ # @rbs node: RuboCop::AST::SendNode
27
+ def check_annotation_alignment(node) #: void
28
+ annotated = attr_arguments(node).filter_map do |arg|
29
+ comment = inline_type_comment(arg.location.line)
30
+ [arg, comment] if comment #: [RuboCop::AST::Node, Parser::Source::Comment]
31
+ end
32
+ return if annotated.size < 2
33
+
34
+ expected_col = annotation_column(node)
35
+ annotated.each do |arg, comment|
36
+ actual_col = comment.location.column
37
+ next if actual_col == expected_col
38
+
39
+ add_offense(comment.source_range) do |corrector|
40
+ correct_alignment(corrector, arg, comment, expected_col)
41
+ end
42
+ end
43
+ end
44
+
45
+ # @rbs corrector: RuboCop::Cop::Corrector
46
+ # @rbs arg: RuboCop::AST::Node
47
+ # @rbs comment: Parser::Source::Comment
48
+ # @rbs expected_col: Integer
49
+ def correct_alignment(corrector, arg, comment, expected_col) #: void # rubocop:disable Metrics/AbcSize
50
+ line = arg.location.line
51
+ line_source = source_code_at(line)
52
+ source = line_source[...comment.location.column] || raise
53
+ content_end_col = source.rstrip.length
54
+ padding = [expected_col - content_end_col, 1].max || raise
55
+
56
+ line_begin = processed_source.buffer.line_range(line).begin_pos
57
+ replace_start = line_begin + content_end_col
58
+ replace_end = line_begin + comment.location.column
59
+
60
+ corrector.replace(range_between(replace_start, replace_end), " " * padding)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -26,106 +26,25 @@ module RuboCop
26
26
  # )
27
27
  #
28
28
  class DataClassCommentAlignment < Base
29
- include ASTUtils
30
- include RangeHelp
31
- include SourceCodeHelper
29
+ include ClassCommentAlignment
32
30
  extend AutoCorrector
33
31
 
34
32
  MSG = "Misaligned inline type annotation for Data attribute."
35
33
 
36
34
  # @rbs node: RuboCop::AST::SendNode
37
35
  def on_send(node) #: void
38
- return unless data_define?(node)
39
- return if folded_data_class?(node)
36
+ return unless struct_like_class?(node)
40
37
 
41
- check_annotation_alignment(node)
38
+ check_comment_alignment(node)
42
39
  end
43
40
 
44
41
  private
45
42
 
46
43
  # @rbs node: RuboCop::AST::SendNode
47
- def data_define?(node) #: bool
44
+ def struct_like_class?(node) #: bool
48
45
  return false unless node.method_name == :define
49
46
 
50
- case (r = node.receiver)
51
- when RuboCop::AST::ConstNode
52
- r.short_name == :Data
53
- else
54
- false
55
- end
56
- end
57
-
58
- # @rbs arg: RuboCop::AST::Node
59
- def data_attribute?(arg) #: bool
60
- arg.sym_type? || arg.str_type?
61
- end
62
-
63
- # @rbs node: RuboCop::AST::SendNode
64
- def data_attributes(node) #: Array[RuboCop::AST::Node]
65
- node.arguments.select { data_attribute?(_1) }
66
- end
67
-
68
- # @rbs node: RuboCop::AST::SendNode
69
- def folded_data_class?(node) #: bool
70
- args = node.arguments
71
- return false if args.empty?
72
-
73
- lines = args.map { _1.location.line }
74
- lines.uniq.length < args.length || lines.include?(node.location.line)
75
- end
76
-
77
- # @rbs node: RuboCop::AST::SendNode
78
- def check_annotation_alignment(node) #: void
79
- annotated = data_attributes(node).filter_map do |arg|
80
- comment = inline_type_comment(arg.location.line)
81
- [arg, comment] if comment #: [RuboCop::AST::Node, Parser::Source::Comment]
82
- end
83
- return if annotated.size < 2
84
-
85
- expected_col = annotation_column(node)
86
- annotated.each do |arg, comment|
87
- actual_col = comment.location.column
88
- next if actual_col == expected_col
89
-
90
- add_offense(comment.source_range) do |corrector|
91
- correct_alignment(corrector, arg, comment, expected_col)
92
- end
93
- end
94
- end
95
-
96
- # @rbs line: Integer
97
- def inline_type_comment(line) #: Parser::Source::Comment?
98
- comment = comment_at(line)
99
- comment if comment&.text&.match?(/\A#:/)
100
- end
101
-
102
- # @rbs node: RuboCop::AST::SendNode
103
- def annotation_column(node) #: Integer
104
- last_arg = node.arguments.last
105
- max_end_col = node.arguments.map do |arg|
106
- comma_length = arg.equal?(last_arg) ? 0 : 1
107
- arg.location.column + source!(arg).length + comma_length
108
- end.max || 0
109
-
110
- max_end_col + 2
111
- end
112
-
113
- # @rbs corrector: RuboCop::Cop::Corrector
114
- # @rbs arg: RuboCop::AST::Node
115
- # @rbs comment: Parser::Source::Comment
116
- # @rbs expected_col: Integer
117
- def correct_alignment(corrector, arg, comment, expected_col) #: void # rubocop:disable Metrics/AbcSize
118
- line = arg.location.line
119
- line_source = source_code_at(line)
120
- source = line_source[...comment.location.column] || raise
121
- content_end_col = source.rstrip.length
122
- padding = [expected_col - content_end_col, 1].max || raise
123
-
124
- line_begin = processed_source.buffer.line_range(line).begin_pos
125
- replace_start = line_begin + content_end_col
126
- replace_end = line_begin + comment.location.column
127
-
128
- corrector.replace(range_between(replace_start, replace_end), " " * padding)
47
+ (r = node.receiver).is_a?(RuboCop::AST::ConstNode) && r.short_name == :Data
129
48
  end
130
49
  end
131
50
  end
@@ -34,7 +34,7 @@ module RuboCop
34
34
 
35
35
  # @rbs node: RuboCop::AST::SendNode
36
36
  def on_send(node) #: void
37
- return unless data_define?(node)
37
+ return unless struct_like_class?(node)
38
38
 
39
39
  block_node = node.parent
40
40
  return unless block_node&.block_type?
@@ -45,11 +45,10 @@ module RuboCop
45
45
  private
46
46
 
47
47
  # @rbs node: RuboCop::AST::SendNode
48
- def data_define?(node) #: bool
48
+ def struct_like_class?(node) #: bool
49
49
  return false unless node.method_name == :define
50
50
 
51
- receiver = node.receiver
52
- receiver.is_a?(RuboCop::AST::ConstNode) && receiver.short_name == :Data
51
+ (r = node.receiver).is_a?(RuboCop::AST::ConstNode) && r.short_name == :Data
53
52
  end
54
53
  end
55
54
  end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ module RbsInline
7
+ # Shared logic for cops that check inline type annotations on struct-like
8
+ # class definitions such as `Data.define` and `Struct.new`.
9
+ #
10
+ # Including cops decide which nodes to check by defining their own matcher
11
+ # (e.g. `struct_like_class?`); this module only provides helpers that operate
12
+ # on an already-matched definition node. Cops may override {#attr_argument?}
13
+ # when the set of attribute arguments differs from the default (e.g.
14
+ # `Struct.new` treats a leading string argument as the struct name rather than
15
+ # an attribute).
16
+ #
17
+ # @rbs module-self RuboCop::Cop::Base
18
+ module DataStructHelper
19
+ include ASTUtils
20
+ include SourceCodeHelper
21
+ include RangeHelp
22
+
23
+ # @rbs arg: RuboCop::AST::Node
24
+ def attr_argument?(arg) #: bool
25
+ arg.sym_type? || arg.str_type?
26
+ end
27
+
28
+ # @rbs node: RuboCop::AST::SendNode
29
+ def attr_arguments(node) #: Array[RuboCop::AST::Node]
30
+ node.arguments.select { attr_argument?(_1) }
31
+ end
32
+
33
+ # @rbs node: RuboCop::AST::SendNode
34
+ def folded?(node) #: bool
35
+ attrs = attr_arguments(node)
36
+ return false if attrs.empty?
37
+
38
+ lines = attrs.map { _1.location.line }
39
+ lines.uniq.length < attrs.length || lines.include?(node.location.line)
40
+ end
41
+
42
+ # @rbs line: Integer
43
+ def inline_type_annotation?(line) #: boolish
44
+ comment = comment_at(line)
45
+ comment&.text&.match?(/\A#:/)
46
+ end
47
+
48
+ # @rbs line: Integer
49
+ def inline_type_comment(line) #: Parser::Source::Comment?
50
+ comment = comment_at(line)
51
+ comment if comment&.text&.match?(/\A#:/)
52
+ end
53
+
54
+ # @rbs line: Integer
55
+ def find_regular_comment(line) #: Parser::Source::Comment?
56
+ comment = comment_at(line)
57
+ comment unless comment&.text&.match?(/\A#:/)
58
+ end
59
+
60
+ # @rbs node: RuboCop::AST::SendNode
61
+ def annotation_column(node) #: Integer
62
+ last_arg = node.arguments.last
63
+ max_end_col = node.arguments.map do |arg|
64
+ comma_length = arg.equal?(last_arg) ? 0 : 1
65
+ arg.location.column + source!(arg).length + comma_length
66
+ end.max || 0
67
+
68
+ max_end_col + 2
69
+ end
70
+
71
+ # @rbs node: RuboCop::AST::SendNode
72
+ def longest_argname(node) #: String
73
+ last_index = node.arguments.size - 1
74
+ args = node.arguments.each_with_index.map { |a, i| i < last_index ? "#{a.source}," : a.source.to_s }
75
+ args.max_by(&:length) || ""
76
+ end
77
+
78
+ # @rbs node: RuboCop::AST::SendNode
79
+ def build_multiline_replacement(node) #: String # rubocop:disable Metrics/AbcSize
80
+ base_indent = source_code_at(node.location.line)[/\A\s*/]
81
+ last_index = node.arguments.size - 1
82
+ longest = longest_argname(node)
83
+
84
+ args_source = node.arguments.each_with_index.map do |arg, i|
85
+ comma = i < last_index ? "," : ""
86
+ prefix = "#{base_indent} #{arg.source}#{comma}"
87
+ padding = " " * (longest.length - source!(arg).length - comma.length + 2)
88
+ attr_argument?(arg) ? "#{prefix}#{padding}#: untyped" : prefix
89
+ end.join("\n")
90
+
91
+ "(\n#{args_source}\n#{base_indent})"
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ module RbsInline
7
+ # Shared behavior for cops that ensure struct-like class attributes have
8
+ # inline type annotations (`Data.define` / `Struct.new`).
9
+ #
10
+ # The including cop matches the definition node (e.g. via `struct_like_class?`)
11
+ # and calls {#check_missing_annotations}.
12
+ #
13
+ # @rbs module-self RuboCop::Cop::Base
14
+ module MissingClassAnnotation
15
+ include DataStructHelper
16
+
17
+ # @rbs node: RuboCop::AST::SendNode
18
+ def check_missing_annotations(node) #: void
19
+ if folded?(node)
20
+ add_offenses_for_folded(node)
21
+ else
22
+ check_multiline(node)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # @rbs node: RuboCop::AST::SendNode
29
+ def add_offenses_for_folded(node) #: void
30
+ corrected = false
31
+ attr_arguments(node).each do |arg|
32
+ if corrected
33
+ add_offense(arg)
34
+ else
35
+ replacement = build_multiline_replacement(node)
36
+ loc = node.loc
37
+ if loc.begin && loc.end
38
+ add_offense(arg) { _1.replace(loc.begin.join(loc.end), replacement) }
39
+ corrected = true
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # @rbs node: RuboCop::AST::SendNode
46
+ def check_multiline(node) #: void
47
+ attr_arguments(node).each do |arg|
48
+ next if inline_type_annotation?(arg.location.line)
49
+
50
+ add_offense(arg) { correct_multiline(_1, node, arg) }
51
+ end
52
+ end
53
+
54
+ # @rbs corrector: RuboCop::Cop::Corrector
55
+ # @rbs node: RuboCop::AST::SendNode
56
+ # @rbs arg: RuboCop::AST::Node
57
+ def correct_multiline(corrector, node, arg) #: void
58
+ existing_comment = find_regular_comment(arg.location.line)
59
+
60
+ if existing_comment
61
+ comment_text = existing_comment.text.sub(/\A#\s*/, "")
62
+ corrector.replace(existing_comment.source_range, "#: untyped -- #{comment_text}")
63
+ else
64
+ insert_annotation(corrector, node, arg)
65
+ end
66
+ end
67
+
68
+ # @rbs corrector: RuboCop::Cop::Corrector
69
+ # @rbs node: RuboCop::AST::SendNode
70
+ # @rbs arg: RuboCop::AST::Node
71
+ def insert_annotation(corrector, node, arg) #: void
72
+ line = arg.location.line
73
+ line_source = source_code_at(line)
74
+ content_end_col = line_source.rstrip.length
75
+ padding = [annotation_column(node) - content_end_col, 1].max || raise
76
+ line_begin = processed_source.buffer.line_range(line).begin_pos
77
+ insert_pos = line_begin + content_end_col
78
+
79
+ corrector.insert_before(range_between(insert_pos, insert_pos), "#{" " * padding}#: untyped")
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end