rubocop-on-rbs 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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