rubocop-on-rbs 0.3.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b70070f22849df33c71ad69c0f2a08c1a6a2d0523c19f779868dda630787de43
4
- data.tar.gz: 43789ae285bc5e4f155ba5641fed3471cf27715e58007c02e26661d853304084
3
+ metadata.gz: 889ce959e28673f250187124bfd7955879317482cfdfb976f4bdb975fc918c5b
4
+ data.tar.gz: 0e0654669928cacd44ca23ad5ece90521ac21660758277fe0af42d3b33377d5b
5
5
  SHA512:
6
- metadata.gz: 5a3dece661499c49c0f62c9db11c5a3ee5d0e926d4aedbced0c27b09fda4425eaea161663d22cbf3edf7f37a12fd83e56350b6832f4d90eeac17d027dd5c6f3f
7
- data.tar.gz: '0662672318876ae0d61e604c3e5e9d7405000fb5632ece629ee7497ba5d45b9e9c0ebd1b11aa687472a07bf4b1fdde988f803975007841c47b21ed313ee3482c'
6
+ metadata.gz: 1556cdd4bb1116f2f49aca75d39ed229f3f3074e33d338528cb051c97cbcd952f2cb4daec81e0186e54c0794d2c265609e56483c7addcf77ad5d648b2fc68a39
7
+ data.tar.gz: c77872b3822c006c59d89a637f59366e63a58beb2a0ac7b8b351c1b4428ba353d7e1c8f2480faa5209258649eb25574008ffcb9ce2f25b815a7540b09da60c35
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2024-06-14
4
+
5
+ * Split RuboCop task by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/6
6
+ * Ignore when return untyped by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/7
7
+ * Split task for steep by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/8
8
+ * Add Style/EmptyArgument by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/9
9
+ * Allow return variable only to use by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/10
10
+ * Remove Style/MergeUntyped by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/11
11
+ * Remove Lint/TypeParamsArity by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/12
12
+ * Regenerate docs by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/13
13
+ * Add RBS/Layout/SpaceAroundOperators by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/14
14
+ * Add RBS/Style/RedundantParentheses by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/15
15
+ * Add RBS/Lint/LiteralIntersection by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/16
16
+
17
+ ## [0.3.0] - 2024-06-11
18
+
19
+ * Implement RedundantOverloadTypeParams instead of UselessOverloadTypeParams by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/1
20
+ * Introduce RBS/Lint/DuplicateOverload by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/2
21
+ * Fix typo by @ydah in https://github.com/ksss/rubocop-on-rbs/pull/3
22
+ * Remove unnecessary Exclude settings in .rubocop.yml by @ydah in https://github.com/ksss/rubocop-on-rbs/pull/4
23
+ * Generate docs for cop by @ksss in https://github.com/ksss/rubocop-on-rbs/pull/5
24
+
3
25
  ## [0.1.0] - 2024-05-15
4
26
 
5
27
  - Initial release
data/README.md CHANGED
@@ -64,6 +64,10 @@ And restart VSCode.
64
64
 
65
65
  ## Departments
66
66
 
67
+ You can see documentation for all cops.
68
+
69
+ https://github.com/ksss/rubocop-on-rbs/blob/main/docs/modules/ROOT/pages/cops.adoc
70
+
67
71
  ### RBS
68
72
 
69
73
  This gem handles many cops.
@@ -74,8 +78,6 @@ RBS:
74
78
  Enabled: true
75
79
  ```
76
80
 
77
- See `config/default.yml` for all Cop.
78
-
79
81
  ### RBS/Layout
80
82
 
81
83
  This department is a collection of relatively minor fixes has been gathered.
data/config/default.yml CHANGED
@@ -59,6 +59,10 @@ RBS/Layout/SpaceAroundBraces:
59
59
  Description: 'Use space around `{}`'
60
60
  Enabled: true
61
61
 
62
+ RBS/Layout/SpaceAroundOperators:
63
+ Description: 'Use space around `|` or `&`'
64
+ Enabled: true
65
+
62
66
  RBS/Layout/SpaceBeforeColon:
63
67
  Description: 'Use space before `:`'
64
68
  Enabled: true
@@ -81,9 +85,9 @@ RBS/Lint/DuplicateOverload:
81
85
  Description: 'Checks that there are no repeated overload bodies'
82
86
  Enabled: true
83
87
 
84
- RBS/Lint/RedundantOverloadTypeParams:
88
+ RBS/Lint/LiteralIntersection:
85
89
  Severity: warning
86
- Description: 'Check redundant overload type params'
90
+ Description: 'Check literal intersection'
87
91
  Enabled: true
88
92
 
89
93
  RBS/Lint/Syntax:
@@ -91,52 +95,10 @@ RBS/Lint/Syntax:
91
95
  Description: 'Check RBS syntax'
92
96
  Enabled: true
93
97
 
94
- RBS/Lint/TypeParamsArity:
98
+ RBS/Lint/UselessOverloadTypeParams:
95
99
  Severity: warning
96
- Description: 'Check type params arity'
97
- Enabled: true
98
- Expects:
99
- # Default expects.
100
- # You can add expects in .rubocop.yml.
101
-
102
- ## class/module
103
- Array: 1
104
- Enumerable: 1
105
- Enumerator: 2
106
- Enumerator::Chain: 1
107
- Enumerator::Generator: 1
108
- Enumerator::Lazy: 2
109
- Enumerator::Product: 1
110
- FrozenError: 1
111
- Hash: 2
112
- KeyError: 2
113
- NameError: 1
114
- NoMatchingPatternKeyError: 2
115
- NoMethodError: 1
116
- ObjectSpace::WeakKeyMap: 2
117
- Range: 1
118
- Set: 1
119
- Struct: 1
120
-
121
- ## interface
122
- Array::_Pattern: 1
123
- Enumerable::_NotFound: 1
124
- Gem::_HashLike: 2
125
- Kernel::_RationalDiv: 1
126
- Marshal::_Proc: 1
127
- String::_MatchAgainst: 2
128
- _Each: 1
129
- _EachEntry: 1
130
- _Range: 1
131
- _ToA: 1
132
- _ToAry: 1
133
- _ToH: 2
134
- _ToHash: 2
135
-
136
- ## type alias
137
- array: 1
138
- hash: 2
139
- range: 1
100
+ Description: 'Check redundant overload type params'
101
+ Enabled: true
140
102
 
141
103
  RBS/Lint/WillSyntaxError:
142
104
  Severity: warning
@@ -160,18 +122,22 @@ RBS/Style/DuplicatedType:
160
122
  Description: 'Check duplicated type'
161
123
  Enabled: true
162
124
 
163
- RBS/Style/InitializeReturnType:
164
- Description: 'Use `void` for initialize method'
125
+ RBS/Style/EmptyArgument:
126
+ Description: 'Use `()` for empty argument'
165
127
  Enabled: true
166
128
 
167
- RBS/Style/MergeUntyped:
168
- Description: 'Merge to `untyped`'
129
+ RBS/Style/InitializeReturnType:
130
+ Description: 'Use `void` for initialize method'
169
131
  Enabled: true
170
132
 
171
133
  RBS/Style/OptionalNil:
172
134
  Description: 'Use nil instead of nil?'
173
135
  Enabled: true
174
136
 
137
+ RBS/Style/RedundantParentheses:
138
+ Description: 'Remove redundant parentheses'
139
+ Enabled: true
140
+
175
141
  RBS/Style/TrueFalse:
176
142
  Description: 'Use bool instead of true | false'
177
143
  Enabled: true
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RBS
6
+ module Layout
7
+ # @example default
8
+ # # bad
9
+ # Integer|String
10
+ #
11
+ # # good
12
+ # Integer | String
13
+ class SpaceAroundOperators < RuboCop::RBS::CopBase
14
+ extend AutoCorrector
15
+
16
+ def on_rbs_def(decl)
17
+ decl.overloads.each do |overload|
18
+ overload.method_type.each_type do |type|
19
+ on_type(type)
20
+ end
21
+ end
22
+ end
23
+
24
+ def on_type(type)
25
+ case type
26
+ when ::RBS::Types::Union
27
+ check_operator(type, '|')
28
+ when ::RBS::Types::Intersection
29
+ check_operator(type, '&')
30
+ end
31
+ type.each_type do |t|
32
+ on_type(t)
33
+ end
34
+ end
35
+
36
+ def check_operator(type, operator)
37
+ type.types.each_cons(2) do |before, after|
38
+ next unless before.location.end_line == after.location.start_line
39
+
40
+ operator_index = type.location.source.index(
41
+ operator,
42
+ before.location.end_pos - type.location.start_pos
43
+ ) or raise
44
+ operator_index += type.location.start_pos
45
+ operator_range = range_between(operator_index, operator_index + 1)
46
+
47
+ before_char = processed_source.raw_source[operator_index - 1]
48
+ if before_char != ' '
49
+ add_offense(operator_range, message: "Use one space before `#{operator}`.") do |corrector|
50
+ corrector.insert_before(operator_range, ' ')
51
+ end
52
+ end
53
+
54
+ after_char = processed_source.raw_source[operator_index + 1]
55
+ if after_char != ' '
56
+ add_offense(operator_range, message: "Use one space after `#{operator}`.") do |corrector|
57
+ corrector.insert_after(operator_range, ' ')
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RBS
6
+ module Lint
7
+ # Checks that there are no repeated overload bodies
8
+ #
9
+ # @example default
10
+ # # bad
11
+ # 1 & 2
12
+ #
13
+ # # bad
14
+ # 1 & _Foo
15
+ #
16
+ class LiteralIntersection < RuboCop::RBS::CopBase
17
+ MSG = "Don't use literals with `&`."
18
+
19
+ def on_rbs_def(decl)
20
+ decl.overloads.each do |overload|
21
+ overload.method_type.each_type do |type|
22
+ check_type(type)
23
+ end
24
+ end
25
+ end
26
+
27
+ def check_type(type)
28
+ on_type([::RBS::Types::Intersection], type) do |intersection|
29
+ check_intersection(intersection)
30
+ end
31
+ end
32
+
33
+ def on_rbs_constant(type)
34
+ check_type(type.type)
35
+ end
36
+ alias on_rbs_global on_rbs_constant
37
+ alias on_rbs_type_alias on_rbs_constant
38
+ alias on_rbs_attribute on_rbs_constant
39
+
40
+ def check_intersection(intersection)
41
+ intersection.types.each do |type|
42
+ check_intersection_child(type)
43
+ end
44
+ end
45
+
46
+ def check_intersection_child(type)
47
+ case type
48
+ when ::RBS::Types::Literal
49
+ range = location_to_range(type.location)
50
+ add_offense(range)
51
+ when ::RBS::Types::Intersection
52
+ check_intersection(type)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -10,33 +10,18 @@ module RuboCop
10
10
  # # bad
11
11
  # def foo: [T] () -> void
12
12
  #
13
- # # bad
14
- # def bar: [T] () -> T
15
- #
16
- # # bad
17
- # def baz: [T] () { () -> T } -> void
18
- #
19
13
  # # good
20
- # def foo: [T] (Array[T]) -> T
21
- class RedundantOverloadTypeParams < RuboCop::RBS::CopBase
22
- MSG = 'Redundant overload type variable - `%<variable>s`.'
14
+ # def foo: [T] (T) -> T
15
+ class UselessOverloadTypeParams < RuboCop::RBS::CopBase
16
+ MSG = 'Useless overload type variable - `%<variable>s`.'
23
17
 
24
18
  def on_rbs_def(decl)
25
19
  decl.overloads.each do |overload|
26
20
  next if overload.method_type.type_params.empty?
27
21
 
28
- type_params = overload.method_type.type_params
22
+ type_params = overload.method_type.type_params.dup
29
23
 
30
- types = []
31
- overload.method_type.type.each_param do |param|
32
- types << param.type
33
- end
34
- overload.method_type.block&.then do |block|
35
- block.type.each_type do |t|
36
- types << t
37
- end
38
- end
39
- types.each do |type|
24
+ overload.method_type.each_type do |type|
40
25
  used_variable_in_type(type) do |var|
41
26
  type_params.delete_if { |type_param| type_param.name == var.name }
42
27
  end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RBS
6
+ module Style
7
+ # @example default
8
+ # # bad
9
+ # def foo: -> void
10
+ #
11
+ # # bad
12
+ # def foo: () { -> void } -> void
13
+ #
14
+ # # bad
15
+ # def foo: () -> ^ -> void
16
+ #
17
+ # # good
18
+ # def foo: () { () -> void } -> ^() -> void
19
+ class EmptyArgument < RuboCop::RBS::CopBase
20
+ class MethodTypeChecker
21
+ include RuboCop::RBS::OnTypeHelper
22
+
23
+ def initialize(base_type: nil, &block)
24
+ @base_type = base_type
25
+ @base = base_type.location.start_pos
26
+ @tokens = tokenize(base_type.location.source)
27
+ @block = block
28
+ end
29
+
30
+ def check
31
+ check_method_argument
32
+ if @base_type.block
33
+ check_block_argument
34
+ end
35
+ @base_type.each_type do |type|
36
+ check_type(type)
37
+ end
38
+ end
39
+
40
+ # [T] () -> void
41
+ def check_method_argument
42
+ if @base_type.type_params.empty?
43
+ will_lparen_token = @tokens[0]
44
+ else
45
+ rbracket_index = @tokens.index do |token|
46
+ token.location.start_pos + @base >= @base_type.type_params.last.location.end_pos
47
+ end or raise
48
+ raise unless @tokens[rbracket_index].type == :pRBRACKET
49
+
50
+ will_lparen_token = @tokens[rbracket_index + 1]
51
+ end
52
+
53
+ if will_lparen_token.type != :pLPAREN
54
+ @block.call(
55
+ @base + will_lparen_token.location.start_pos,
56
+ @base + will_lparen_token.location.start_pos + 1
57
+ )
58
+ end
59
+ end
60
+
61
+ # { () [self: instance] -> void } -> void
62
+ def check_block_argument
63
+ return unless @base_type.block
64
+ return unless @base_type.block.type.each_param.first.nil?
65
+
66
+ if @base_type.block.self_type
67
+ self_type_index = bsearch_token_index(@base_type.block.self_type.location.start_pos)
68
+ # ) [self:
69
+ # ^ ^^ ^ => pRPAREN, pLBRACKET, kSELF, pCOLON
70
+ rparen = @tokens[self_type_index - 4]
71
+ after_rparen = @tokens[self_type_index - 3]
72
+ else
73
+ block_arrow_index = @tokens.find_index { |t| t.type == :pARROW } or raise
74
+ rparen = @tokens[block_arrow_index - 1]
75
+ after_rparen = @tokens[block_arrow_index]
76
+ end
77
+
78
+ if rparen.type != :pRPAREN
79
+ @block.call(
80
+ @base + after_rparen.location.start_pos,
81
+ @base + after_rparen.location.start_pos + 1
82
+ )
83
+ end
84
+ end
85
+
86
+ def check_type(type = @base_type)
87
+ on_type([::RBS::Types::Proc], type) do |proc_type|
88
+ check_proc(proc_type)
89
+ end
90
+ end
91
+
92
+ def check_proc(type)
93
+ proc_start_index = bsearch_token_index(type.location.start_pos)
94
+ proc_end_index = bsearch_token_index(type.location.end_pos)
95
+ if @tokens[proc_start_index + 1].type != :pLPAREN
96
+ @block.call(
97
+ @base + @tokens[proc_start_index + 1].location.start_pos,
98
+ @base + @tokens[proc_start_index + 1].location.start_pos + 1
99
+ )
100
+ end
101
+
102
+ if type.block
103
+ if type.block&.self_type
104
+ self_type_index = bsearch_token_index(type.block.self_type.location.start_pos)
105
+ # ) [self:
106
+ # ^ ^^ ^ => pRPAREN, pLBRACKET, kSELF, pCOLON
107
+ rparen = @tokens[self_type_index - 4]
108
+ after_rparen = @tokens[self_type_index - 3]
109
+ else
110
+ block_arrow_index = @tokens[proc_start_index...proc_end_index].find_index { |t|
111
+ t.type == :pARROW
112
+ } or raise
113
+ block_arrow_index += proc_start_index
114
+ rparen = @tokens[block_arrow_index - 1]
115
+ after_rparen = @tokens[block_arrow_index]
116
+ end
117
+
118
+ if rparen.type != :pRPAREN
119
+ @block.call(
120
+ @base + after_rparen.location.start_pos,
121
+ @base + after_rparen.location.start_pos + 1
122
+ )
123
+ end
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def bsearch_token_index(pos)
130
+ @tokens.bsearch_index do |token|
131
+ token.location.start_pos + @base >= pos
132
+ end or raise
133
+ end
134
+
135
+ def tokenize(source)
136
+ ::RBS::Parser.lex(source).value.reject { |t| t.type == :tTRIVIA }
137
+ end
138
+ end
139
+
140
+ extend AutoCorrector
141
+
142
+ MSG = 'Insert `()` when empty argument'
143
+
144
+ def on_rbs_def(decl)
145
+ decl.overloads.each do |overload|
146
+ MethodTypeChecker.new(base_type: overload.method_type) do |s, e|
147
+ range = range_between(s, e)
148
+ add_offense(range) do |corrector|
149
+ corrector.insert_before(range, '() ')
150
+ end
151
+ end.check
152
+ end
153
+ end
154
+
155
+ def on_rbs_constant(const)
156
+ MethodTypeChecker.new(base_type: const.type) do |s, e|
157
+ range = range_between(s, e)
158
+ add_offense(range) do |corrector|
159
+ corrector.insert_before(range, '() ')
160
+ end
161
+ end.check_type
162
+ end
163
+ alias on_rbs_global on_rbs_constant
164
+ alias on_rbs_type_alias on_rbs_constant
165
+ alias on_rbs_attribute on_rbs_constant
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -6,18 +6,17 @@ module RuboCop
6
6
  module Style
7
7
  # @example default
8
8
  # # bad
9
- # def initialize: () -> untyped
10
- #
11
- # # bad
12
9
  # def initialize: () -> nil
13
10
  #
14
11
  # # good
12
+ # def initialize: () -> untyped
13
+ #
14
+ # # good
15
15
  # def initialize: () -> void
16
16
  class InitializeReturnType < RuboCop::RBS::CopBase
17
17
  extend AutoCorrector
18
18
  MSG = '`#initialize` method should return `void`'
19
19
 
20
- # @sig decl: ::RBS::AST::Members::MethodDefinition
21
20
  def on_rbs_def(decl)
22
21
  return unless decl.name == :initialize
23
22
  return unless decl.kind == :instance
@@ -25,6 +24,7 @@ module RuboCop
25
24
 
26
25
  decl.overloads.each do |overload|
27
26
  return_type = overload.method_type.type.return_type
27
+ next if return_type.is_a?(::RBS::Types::Bases::Any)
28
28
  next if return_type.is_a?(::RBS::Types::Bases::Void)
29
29
 
30
30
  range = location_to_range(return_type.location)
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RBS
6
+ module Style
7
+ # @example default
8
+ # # bad
9
+ # def foo: () -> (bool)
10
+ #
11
+ # # bad
12
+ # def foo: (((true | false))) -> void
13
+ #
14
+ # # good
15
+ # def foo: () -> bool
16
+ #
17
+ # # good
18
+ # def foo: ((true | false)) -> bool
19
+ class RedundantParentheses < RuboCop::RBS::CopBase
20
+ module BeforeTokenIfLparen
21
+ def before_token_if_lparen(tokens, base, fun)
22
+ first_param = fun.each_param.first
23
+ return unless first_param
24
+
25
+ b, _ = token_before_after(tokens, first_param.location.start_pos - base)
26
+ return unless b&.type == :pLPAREN
27
+
28
+ yield b
29
+ end
30
+
31
+ private
32
+
33
+ def token_before_after(tokens, pos)
34
+ token_index = tokens.bsearch_index do |t|
35
+ t.location.start_pos >= pos
36
+ end
37
+ return unless token_index
38
+
39
+ [
40
+ tokens[token_index - 1],
41
+ tokens[token_index + 1]
42
+ ]
43
+ end
44
+ end
45
+
46
+ class ParenChecker
47
+ include BeforeTokenIfLparen
48
+ include RangeHelp
49
+ attr_reader :processed_source
50
+
51
+ def initialize(processed_source:, base:, tokens:, type:, skip:, cop:)
52
+ @processed_source = processed_source
53
+ @base = base
54
+ @tokens = tokens
55
+ @type = type
56
+ @skip = skip
57
+ @cop = cop
58
+ end
59
+
60
+ def check
61
+ @cop.on_type([::RBS::Types::Proc], @type) do |proc_type|
62
+ before_token_if_lparen(@tokens, @base, proc_type.type) do |b|
63
+ @skip << (b.location.start_pos + @base)
64
+ end
65
+ if proc_type.block
66
+ before_token_if_lparen(@tokens, @base, proc_type.block.type) do |b|
67
+ @skip << (b.location.start_pos + @base)
68
+ end
69
+ end
70
+ end
71
+
72
+ excludes = [
73
+ ::RBS::Types::Union,
74
+ ::RBS::Types::Intersection,
75
+ ::RBS::Types::Proc
76
+ ]
77
+ @cop.on_not_type(excludes, @type) do |type|
78
+ check_parentheses(type)
79
+ end
80
+ end
81
+
82
+ def check_parentheses(type)
83
+ type_token_start_index = @tokens.bsearch_index do |token|
84
+ (token.location.start_pos + @base) >= type.location.start_pos
85
+ end or raise
86
+ before_token = @tokens[type_token_start_index - 1]
87
+ return if @skip.include?(before_token.location.start_pos + @base)
88
+ return if before_token&.type != :pLPAREN
89
+
90
+ type_token_end_index = @tokens.bsearch_index do |token|
91
+ (token.location.start_pos + @base) >= type.location.end_pos
92
+ end or raise
93
+ after_token = @tokens[type_token_end_index]
94
+ return if after_token&.type != :pRPAREN
95
+
96
+ range = range_between(before_token.location.start_pos + @base, after_token.location.end_pos + @base)
97
+ @cop.add_offense(range, message: "Don't use parentheses around simple type.") do |corrector|
98
+ corrector.remove(range_between(before_token.location.start_pos + @base,
99
+ before_token.location.end_pos + @base))
100
+ corrector.remove(range_between(after_token.location.start_pos + @base,
101
+ after_token.location.end_pos + @base))
102
+ end
103
+ end
104
+ end
105
+
106
+ include BeforeTokenIfLparen
107
+ extend AutoCorrector
108
+
109
+ def on_rbs_def(decl)
110
+ base = decl.location.start_pos
111
+ tokens = tokenize(decl.location.source)
112
+ skip = Set.new
113
+ decl.overloads.each do |overload|
114
+ before_token_if_lparen(tokens, base, overload.method_type.type) do |b|
115
+ skip << (b.location.start_pos + base)
116
+ end
117
+ if overload.method_type.block
118
+ before_token_if_lparen(tokens, base, overload.method_type.block.type) do |b|
119
+ skip << (b.location.start_pos + base)
120
+ end
121
+ end
122
+ overload.method_type.each_type do |type|
123
+ check_type(tokens:, type:, base:, skip:)
124
+ end
125
+ end
126
+ end
127
+
128
+ def check_type(tokens:, type:, base:, skip: Set.new)
129
+ ParenChecker.new(
130
+ processed_source:,
131
+ base:,
132
+ tokens:,
133
+ type:,
134
+ skip:,
135
+ cop: self
136
+ ).check
137
+ end
138
+
139
+ def on_rbs_constant(const)
140
+ tokens = tokenize(const.location.source)
141
+ type = const.type
142
+ base = const.location.start_pos
143
+ check_type(tokens:, type:, base:)
144
+ end
145
+ alias on_rbs_global on_rbs_constant
146
+ alias on_rbs_type_alias on_rbs_constant
147
+ alias on_rbs_attribute on_rbs_constant
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -10,20 +10,22 @@ require_relative 'rbs/layout/indentation_width'
10
10
  require_relative 'rbs/layout/overload_indentation'
11
11
  require_relative 'rbs/layout/space_around_arrow'
12
12
  require_relative 'rbs/layout/space_around_braces'
13
+ require_relative 'rbs/layout/space_around_operators'
13
14
  require_relative 'rbs/layout/space_before_colon'
14
15
  require_relative 'rbs/layout/space_before_overload'
15
16
  require_relative 'rbs/layout/trailing_whitespace'
16
17
 
17
18
  require_relative 'rbs/lint/duplicate_overload'
18
- require_relative 'rbs/lint/redundant_overload_type_params'
19
+ require_relative 'rbs/lint/literal_intersection'
20
+ require_relative 'rbs/lint/useless_overload_type_params'
19
21
  require_relative 'rbs/lint/syntax'
20
- require_relative 'rbs/lint/type_params_arity'
21
22
  require_relative 'rbs/lint/will_syntax_error'
22
23
 
23
24
  require_relative 'rbs/style/block_return_boolish'
24
- require_relative 'rbs/style/true_false'
25
25
  require_relative 'rbs/style/classic_type'
26
- require_relative 'rbs/style/optional_nil'
27
26
  require_relative 'rbs/style/duplicated_type'
27
+ require_relative 'rbs/style/empty_argument'
28
28
  require_relative 'rbs/style/initialize_return_type'
29
- require_relative 'rbs/style/merge_untyped'
29
+ require_relative 'rbs/style/optional_nil'
30
+ require_relative 'rbs/style/redundant_parentheses'
31
+ require_relative 'rbs/style/true_false'
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'zlib'
4
+
3
5
  module RuboCop
4
6
  module RBS
5
7
  # Base class for cops that operate on RBS signatures.
6
8
  class CopBase < RuboCop::Cop::Base
7
9
  include RuboCop::Cop::RangeHelp
10
+ include RuboCop::RBS::OnTypeHelper
8
11
 
9
12
  attr_reader :processed_rbs_source
10
13
 
@@ -19,11 +22,31 @@ module RuboCop
19
22
  investigation_rbs()
20
23
  end
21
24
 
25
+ @@cache = {}
26
+ def parse_rbs
27
+ buffer = rbs_buffer()
28
+ @processed_rbs_source = RuboCop::RBS::ProcessedRBSSource.new(buffer)
29
+ end
30
+
22
31
  def investigation_rbs
23
32
  return unless processed_source.buffer.name.then { |n| n.end_with?(".rbs") || n == "(string)" }
24
33
 
25
- buffer = rbs_buffer()
26
- @processed_rbs_source = RuboCop::RBS::ProcessedRBSSource.new(buffer)
34
+ if processed_source.buffer.name == "(string)"
35
+ parse_rbs
36
+ else
37
+ crc32 = Zlib.crc32(processed_source.raw_source)
38
+ hit_path = @@cache[processed_source.buffer.name]
39
+ if hit_path
40
+ if hit_crc32 = hit_path[crc32]
41
+ @processed_rbs_source = hit_crc32
42
+ else
43
+ hit_path.clear # Other key expect clear by GC
44
+ hit_path[crc32] = parse_rbs
45
+ end
46
+ else
47
+ (@@cache[processed_source.buffer.name] ||= {})[crc32] = parse_rbs
48
+ end
49
+ end
27
50
 
28
51
  if processed_rbs_source.error
29
52
  on_rbs_parsing_error()
@@ -86,6 +109,10 @@ module RuboCop
86
109
  def location_to_range(location)
87
110
  range_between(location.start_pos, location.end_pos)
88
111
  end
112
+
113
+ def tokenize(source)
114
+ ::RBS::Parser.lex(source).value.reject { |t| t.type == :tTRIVIA }
115
+ end
89
116
  end
90
117
  end
91
118
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RBS
5
+ module OnTypeHelper
6
+ def on_type(types, type, &block)
7
+ case type
8
+ when *types
9
+ yield type
10
+ end
11
+ type.each_type do |t|
12
+ on_type(types, t, &block)
13
+ end
14
+ end
15
+
16
+ def on_not_type(types, type, &block)
17
+ case type
18
+ when *types
19
+ # not
20
+ else
21
+ yield type
22
+ end
23
+ type.each_type do |t|
24
+ on_not_type(types, t, &block)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module RBS
5
- VERSION = '0.3.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
@@ -4,6 +4,7 @@ require 'rubocop'
4
4
 
5
5
  require_relative 'rubocop/rbs'
6
6
  require_relative 'rubocop/rbs/version'
7
+ require_relative 'rubocop/rbs/on_type_helper'
7
8
  require_relative 'rubocop/rbs/cop_base'
8
9
  require_relative 'rubocop/rbs/inject'
9
10
  require_relative 'rubocop/rbs/processed_rbs_source'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-on-rbs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ksss
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-11 00:00:00.000000000 Z
11
+ date: 2024-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbs
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.41'
41
+ - !ruby/object:Gem::Dependency
42
+ name: zlib
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: RuboCop extension for RBS file.
42
56
  email:
43
57
  - co000ri@gmail.com
@@ -59,25 +73,28 @@ files:
59
73
  - lib/rubocop/cop/rbs/layout/overload_indentation.rb
60
74
  - lib/rubocop/cop/rbs/layout/space_around_arrow.rb
61
75
  - lib/rubocop/cop/rbs/layout/space_around_braces.rb
76
+ - lib/rubocop/cop/rbs/layout/space_around_operators.rb
62
77
  - lib/rubocop/cop/rbs/layout/space_before_colon.rb
63
78
  - lib/rubocop/cop/rbs/layout/space_before_overload.rb
64
79
  - lib/rubocop/cop/rbs/layout/trailing_whitespace.rb
65
80
  - lib/rubocop/cop/rbs/lint/duplicate_overload.rb
66
- - lib/rubocop/cop/rbs/lint/redundant_overload_type_params.rb
81
+ - lib/rubocop/cop/rbs/lint/literal_intersection.rb
67
82
  - lib/rubocop/cop/rbs/lint/syntax.rb
68
- - lib/rubocop/cop/rbs/lint/type_params_arity.rb
83
+ - lib/rubocop/cop/rbs/lint/useless_overload_type_params.rb
69
84
  - lib/rubocop/cop/rbs/lint/will_syntax_error.rb
70
85
  - lib/rubocop/cop/rbs/style/block_return_boolish.rb
71
86
  - lib/rubocop/cop/rbs/style/classic_type.rb
72
87
  - lib/rubocop/cop/rbs/style/duplicated_type.rb
88
+ - lib/rubocop/cop/rbs/style/empty_argument.rb
73
89
  - lib/rubocop/cop/rbs/style/initialize_return_type.rb
74
- - lib/rubocop/cop/rbs/style/merge_untyped.rb
75
90
  - lib/rubocop/cop/rbs/style/optional_nil.rb
91
+ - lib/rubocop/cop/rbs/style/redundant_parentheses.rb
76
92
  - lib/rubocop/cop/rbs/style/true_false.rb
77
93
  - lib/rubocop/cop/rbs_cops.rb
78
94
  - lib/rubocop/rbs.rb
79
95
  - lib/rubocop/rbs/cop_base.rb
80
96
  - lib/rubocop/rbs/inject.rb
97
+ - lib/rubocop/rbs/on_type_helper.rb
81
98
  - lib/rubocop/rbs/processed_rbs_source.rb
82
99
  - lib/rubocop/rbs/version.rb
83
100
  homepage: https://github.com/ksss/rubocop-on-rbs
@@ -1,170 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module RBS
6
- module Lint
7
- # This cop checks the arity of type parameters arity.
8
- # You can add expect settings in your .rubocop.yml.
9
- #
10
- # @example Expects settings
11
- # RBS/Lint/TypeParamsArity:
12
- # Expects:
13
- # Your::Class: 1
14
- #
15
- # @example default
16
- # # bad
17
- # type a = Array[Integer, String, Symbol]
18
- #
19
- # # bad
20
- # class Foo
21
- # include Enumerable
22
- # end
23
- class TypeParamsArity < RuboCop::RBS::CopBase
24
- Types = ::RBS::Types
25
-
26
- def on_rbs_module(decl)
27
- check_type_params(decl)
28
- decl.self_types.each do |self_type|
29
- check(
30
- name: self_type.name,
31
- args: self_type.args,
32
- location: self_type.location
33
- )
34
- end
35
- check_each_mixin(decl)
36
- end
37
-
38
- def on_rbs_class(decl)
39
- check_type_params(decl)
40
- if decl.super_class
41
- check(
42
- name: decl.super_class.name,
43
- args: decl.super_class.args,
44
- location: decl.super_class.location
45
- )
46
- end
47
- check_each_mixin(decl)
48
- end
49
-
50
- def on_rbs_interface(decl)
51
- check_type_params(decl)
52
- check_each_mixin(decl)
53
- end
54
-
55
- def on_rbs_constant(const)
56
- check_type(const.type)
57
- end
58
-
59
- def on_rbs_global(global)
60
- check_type(global.type)
61
- end
62
-
63
- def on_rbs_type_alias(decl)
64
- check_type_params(decl)
65
- check_type(decl.type)
66
- decl.type.each_type do |type|
67
- check_type(type)
68
- end
69
- end
70
-
71
- def on_rbs_def(member)
72
- member.overloads.each do |overload|
73
- overload.method_type.each_type do |type|
74
- check_type(type)
75
- end
76
- end
77
- end
78
-
79
- def on_rbs_attribute(attr)
80
- check_type(attr.type)
81
- end
82
-
83
- def check_each_mixin(decl)
84
- decl.each_mixin do |mixin|
85
- check(
86
- name: mixin.name,
87
- args: mixin.args,
88
- location: mixin.location
89
- )
90
- end
91
- end
92
-
93
- def check_type_params(decl)
94
- decl.type_params.each do |type_param|
95
- if type_param.upper_bound
96
- check(
97
- name: type_param.upper_bound.name,
98
- args: type_param.upper_bound.args,
99
- location: type_param.upper_bound.location
100
- )
101
- end
102
- end
103
- end
104
-
105
- def check_type(type)
106
- case type
107
- when Types::Record,
108
- Types::Tuple,
109
- Types::Union,
110
- Types::Intersection,
111
- Types::Optional,
112
- Types::Proc
113
- type.each_type.each do |t|
114
- check_type(t)
115
- end
116
- when Types::Interface,
117
- Types::Alias,
118
- Types::ClassInstance
119
- check(
120
- name: type.name,
121
- args: type.args,
122
- location: type.location,
123
- )
124
- end
125
- end
126
-
127
- def check(name:, args:, location:)
128
- return unless name.absolute?
129
- return unless location
130
-
131
- expect_size = expects[name.to_s]
132
- return unless expect_size
133
- return unless expect_size != args.size
134
-
135
- message = if args.size == 0
136
- "Type `#{name}` is generic but used as a non generic type."
137
- else
138
- "Type `#{name}` expects #{expect_size} arguments, but #{args.size} arguments are given."
139
- end
140
- add_offense(
141
- location_to_range(location),
142
- message: message,
143
- severity: :error
144
- )
145
- end
146
-
147
- def expects
148
- @expects ||= begin
149
- expects = cop_config['Expects']
150
- raise "Expects must be a hash" unless expects.is_a?(Hash)
151
-
152
- unless expects.all? { |k, _| k.is_a?(String) }
153
- raise "[RBS/Lint/TypeParamsArity] Keys of Expects must be strings"
154
- end
155
- unless expects.all? { |_, v| v.is_a?(Integer) }
156
- raise "[RBS/Lint/TypeParamsArity] Values of Expects must be integers"
157
- end
158
-
159
- expects.transform_keys! do |k|
160
- k.start_with?('::') ? k : "::#{k}"
161
- end
162
-
163
- expects
164
- end
165
- end
166
- end
167
- end
168
- end
169
- end
170
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module Cop
5
- module RBS
6
- module Style
7
- # @example default
8
- # # bad
9
- # def foo: (untyped?) -> untyped?
10
- #
11
- # # bad
12
- # def foo: (Integer | untyped) -> (Integer | untyped)
13
- #
14
- # # bad
15
- # def foo: (Integer & untyped) -> (Integer & untyped)
16
- #
17
- # # good
18
- # def foo: (untyped) -> untyped
19
- class MergeUntyped < RuboCop::RBS::CopBase
20
- extend AutoCorrector
21
-
22
- # @sig decl: ::RBS::AST::Members::MethodDefinition
23
- def on_rbs_def(decl)
24
- decl.overloads.each do |overload|
25
- overload.method_type.type.tap do |fun|
26
- fun.each_param do |param|
27
- check_type(param.type)
28
- end
29
- check_type(fun.return_type)
30
- end
31
- end
32
- end
33
-
34
- def check_type(type)
35
- find_replacement(type) do |method, t, replaced|
36
- case method
37
- when :replace
38
- range = location_to_range(t.location)
39
- add_offense(range, message: "Use `#{replaced}` instead of `#{t}`") do |corrector|
40
- corrector.replace(range, replaced.to_s)
41
- end
42
- when :remove
43
- range = t
44
- add_offense(range, message: "Remove `?` in Optional") do |corrector|
45
- corrector.remove(range)
46
- end
47
- end
48
- end
49
- end
50
-
51
- def find_replacement(type, &block)
52
- case type
53
- when ::RBS::Types::Optional
54
- case type.type
55
- when ::RBS::Types::Bases::Any,
56
- ::RBS::Types::Bases::Nil,
57
- ::RBS::Types::Bases::Void,
58
- ::RBS::Types::Bases::Top,
59
- ::RBS::Types::Bases::Bottom
60
- # untyped? => untyped
61
- block.call([:replace, type, type.type])
62
- when ::RBS::Types::Optional
63
- # (Integer?)? => Integer?
64
- range = Parser::Source::Range.new(processed_source.buffer, type.type.location.end_pos - 1,
65
- type.type.location.end_pos)
66
- block.call([:remove, range])
67
- find_replacement(type.type, &block)
68
- when ::RBS::Types::Union, ::RBS::Types::Intersection
69
- find_replacement(type.type, &block)
70
- end
71
- when ::RBS::Types::Union, ::RBS::Types::Intersection
72
- # (untyped | Integer) => untyped
73
- # (untyped & Integer) => untyped
74
- if type.types.any? { |t| t.is_a?(::RBS::Types::Bases::Any) }
75
- block.call([:replace, type, 'untyped'])
76
- end
77
-
78
- uniqed = type.types.uniq
79
- if uniqed.size < type.types.size
80
- block.call([:replace, type, type.class.new(types: uniqed, location: nil)])
81
- end
82
- type.types.each do |t|
83
- find_replacement(t, &block)
84
- end
85
- end
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end