rubocop-graphql 0.13.0 → 0.14.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: d809ed60d2d98c5f7f84a3555d8ad24ad2c2e1ece6a5fed616b6698260981d29
4
- data.tar.gz: e62936fc399419ee72070e658be32660763a96144776727e183007aa53ad0bd8
3
+ metadata.gz: d8c5edb3e37ffc8245916608bb37f79dc28afb39dde464477e5b31e7f92aef59
4
+ data.tar.gz: 3fc868fb4e1ee8cdb0fed44c35ae98899e83eb605e0618faba161f65344554b4
5
5
  SHA512:
6
- metadata.gz: 8c508f31ceb2e6f1290ef0220896019c5f3ff007dbd2ec651ba44ad5cc422680ec761ccb6739a7e90f243cc434b787f849d872143c6e709967deeeb81e6500ec
7
- data.tar.gz: ccd1741a8b20403f5a904aa321526fb089f4af357b1a8cce37547221f11ef1fa4f407169aedb35923b85c4b5dc5eb0bba76f74ce7b585b260a0d90549c2d7dfb
6
+ metadata.gz: 96f21cb69d2a7fc4c5c084e4859756fc7f525fc29e5c7c2dd4bf5fb1233d039ff3932d5783cdd10f91b0cee15c5cc8c91e94e3fec2f07ee0d09fdbc4b32e2799
7
+ data.tar.gz: c88d409a7c67630c3f68d98fb6d78418be4c3c99d601470f9732ed456b4ee86f3f2439323c019b82eaa52f2b613aa01fdf14cc195304eb3fc0a7c6bb3bec6774
@@ -44,11 +44,13 @@ module RuboCop
44
44
  # object.contact_data.last_name
45
45
  # end
46
46
  # end
47
- class FieldDefinitions < Base
47
+ class FieldDefinitions < Base # rubocop:disable Metrics/ClassLength
48
48
  extend AutoCorrector
49
49
  include ConfigurableEnforcedStyle
50
50
  include RuboCop::GraphQL::NodePattern
51
51
  include RuboCop::Cop::RangeHelp
52
+ include RuboCop::GraphQL::Sorbet
53
+ include RuboCop::GraphQL::Heredoc
52
54
 
53
55
  def_node_matcher :field_kwargs, <<~PATTERN
54
56
  (send nil? :field
@@ -101,14 +103,14 @@ module RuboCop
101
103
  field_definition?(node) || field_definition_with_body?(node)
102
104
  end
103
105
 
104
- source_to_insert = "\n#{indent(node)}#{node.source}"
105
- corrector.insert_after(first_field.loc.expression, source_to_insert)
106
+ insert_new_resolver(corrector, first_field, node)
106
107
 
107
- range = range_with_surrounding_space(range: node.loc.expression, side: :left)
108
- corrector.remove(range)
108
+ remove_old_resolver(corrector, node)
109
109
  end
110
110
 
111
111
  RESOLVER_AFTER_FIELD_MSG = "Define resolver method after field definition."
112
+ RESOLVER_AFTER_LAST_FIELD_MSG = "Define resolver method after last field definition " \
113
+ "sharing resolver method."
112
114
 
113
115
  def check_resolver_is_defined_after_definition(field)
114
116
  return if field.kwargs.resolver || field.kwargs.method || field.kwargs.hash_key
@@ -116,6 +118,22 @@ module RuboCop
116
118
  method_definition = field.schema_member.find_method_definition(field.resolver_method_name)
117
119
  return unless method_definition
118
120
 
121
+ fields_with_same_resolver = fields_with_same_resolver(field, method_definition)
122
+ return unless field.name == fields_with_same_resolver.last.name
123
+
124
+ return if resolver_defined_after_definition?(field, method_definition)
125
+
126
+ add_offense(field.node,
127
+ message: offense_message(fields_with_same_resolver.one?)) do |corrector|
128
+ place_resolver_after_field_definition(corrector, field.node)
129
+ end
130
+ end
131
+
132
+ def offense_message(single_field_using_resolver)
133
+ single_field_using_resolver ? RESOLVER_AFTER_FIELD_MSG : RESOLVER_AFTER_LAST_FIELD_MSG
134
+ end
135
+
136
+ def resolver_defined_after_definition?(field, method_definition)
119
137
  field_sibling_index =
120
138
  if field_definition_with_body?(field.parent)
121
139
  field.parent.sibling_index
@@ -123,28 +141,80 @@ module RuboCop
123
141
  field.sibling_index
124
142
  end
125
143
 
126
- return if method_definition.sibling_index - field_sibling_index == 1
144
+ field_to_resolver_offset = method_definition.sibling_index - field_sibling_index
127
145
 
128
- add_offense(field.node, message: RESOLVER_AFTER_FIELD_MSG) do |corrector|
129
- place_resolver_after_definitions(corrector, field.node)
146
+ case field_to_resolver_offset
147
+ when 1 # resolver is immediately after field definition
148
+ return true
149
+ when 2 # there is a node between the field definition and its resolver
150
+ return true if has_sorbet_signature?(method_definition)
130
151
  end
152
+
153
+ false
131
154
  end
132
155
 
133
- def place_resolver_after_definitions(corrector, node)
156
+ def fields_with_same_resolver(field, resolver)
157
+ fields = field.schema_member.body
158
+ .select { |node| field?(node) }
159
+ .map do |node|
160
+ field = field_definition_with_body?(node) ? node.children.first : node
161
+ RuboCop::GraphQL::Field.new(field)
162
+ end
163
+
164
+ [].tap do |fields_with_same_resolver|
165
+ fields.each do |field|
166
+ return [field] if field.name == resolver.method_name
167
+ next if field.kwargs.resolver_method_name != resolver.method_name
168
+
169
+ fields_with_same_resolver << field
170
+ end
171
+ end
172
+ end
173
+
174
+ def place_resolver_after_field_definition(corrector, node)
134
175
  field = RuboCop::GraphQL::Field.new(node)
135
176
 
136
- method_definition = field.schema_member.find_method_definition(field.resolver_method_name)
177
+ resolver_method_name = field.resolver_method_name
178
+ resolver_definition = field.schema_member.find_method_definition(resolver_method_name)
137
179
 
138
180
  field_definition = field_definition_with_body?(node.parent) ? node.parent : node
139
181
 
140
- source_to_insert = "#{indent(method_definition)}#{field_definition.source}\n\n"
141
- method_range = range_by_whole_lines(method_definition.loc.expression)
142
- corrector.insert_before(method_range, source_to_insert)
182
+ insert_new_resolver(corrector, field_definition, resolver_definition)
143
183
 
184
+ remove_old_resolver(corrector, resolver_definition)
185
+ end
186
+
187
+ def insert_new_resolver(corrector, field_definition, resolver_definition)
188
+ source_to_insert =
189
+ "\n#{signature_to_insert(resolver_definition)}\n" \
190
+ "#{indent(resolver_definition)}#{resolver_definition.source}\n"
191
+
192
+ field_definition_range = range_including_heredoc(field_definition)
193
+ corrector.insert_after(field_definition_range, source_to_insert)
194
+ end
195
+
196
+ def remove_old_resolver(corrector, resolver_definition)
144
197
  range_to_remove = range_with_surrounding_space(
145
- range: field_definition.loc.expression, side: :left
198
+ range: resolver_definition.loc.expression, side: :left
146
199
  )
147
200
  corrector.remove(range_to_remove)
201
+
202
+ resolver_signature = sorbet_signature_for(resolver_definition)
203
+
204
+ return unless resolver_signature
205
+
206
+ range_to_remove = range_with_surrounding_space(
207
+ range: resolver_signature.loc.expression, side: :left
208
+ )
209
+ corrector.remove(range_to_remove)
210
+ end
211
+
212
+ def signature_to_insert(node)
213
+ signature = sorbet_signature_for(node)
214
+
215
+ return unless signature
216
+
217
+ "\n#{indent(signature)}#{signature.source}"
148
218
  end
149
219
 
150
220
  def indent(node)
@@ -47,10 +47,13 @@ module RuboCop
47
47
  field = RuboCop::GraphQL::Field.new(node)
48
48
  method_definition = resolver_method_definition_for(field)
49
49
 
50
- if (suggested_hash_key_name = hash_key_to_use(method_definition))
51
- add_offense(node, message: message(suggested_hash_key_name)) do |corrector|
52
- autocorrect(corrector, node)
53
- end
50
+ suggested_hash_key_name = hash_key_to_use(method_definition)
51
+
52
+ return if suggested_hash_key_name.nil?
53
+ return if RuboCop::GraphQL::Field::CONFLICT_FIELD_NAMES.include?(suggested_hash_key_name)
54
+
55
+ add_offense(node, message: message(suggested_hash_key_name)) do |corrector|
56
+ autocorrect(corrector, node)
54
57
  end
55
58
  end
56
59
 
@@ -46,10 +46,13 @@ module RuboCop
46
46
  field = RuboCop::GraphQL::Field.new(node)
47
47
  method_definition = suggest_method_name_for(field)
48
48
 
49
- if (suggested_method_name = method_to_use(method_definition))
50
- add_offense(node, message: message(suggested_method_name)) do |corrector|
51
- autocorrect(corrector, node)
52
- end
49
+ suggested_method_name = method_to_use(method_definition)
50
+
51
+ return if suggested_method_name.nil?
52
+ return if RuboCop::GraphQL::Field::CONFLICT_FIELD_NAMES.include?(suggested_method_name)
53
+
54
+ add_offense(node, message: message(suggested_method_name)) do |corrector|
55
+ autocorrect(corrector, node)
53
56
  end
54
57
  end
55
58
 
@@ -52,7 +52,7 @@ module RuboCop
52
52
 
53
53
  def has_description?(node)
54
54
  has_i18n_description?(node) ||
55
- description_kwarg?(node)
55
+ description_method_call?(node)
56
56
  end
57
57
 
58
58
  def child_nodes(node)
@@ -67,10 +67,7 @@ module RuboCop
67
67
 
68
68
  def on_class(node)
69
69
  resolve_method_node = find_resolve_method_node(node)
70
- return if resolve_method_node.nil? ||
71
- resolve_method_node.arguments.any? do |arg|
72
- arg.arg_type? || arg.kwrestarg_type?
73
- end
70
+ return if resolve_method_node.nil? || ignore_arguments_type?(resolve_method_node)
74
71
 
75
72
  declared_arg_nodes = find_declared_arg_nodes(node)
76
73
  return unless declared_arg_nodes.any?
@@ -109,6 +106,12 @@ module RuboCop
109
106
  end
110
107
  end
111
108
 
109
+ def ignore_arguments_type?(resolve_method_node)
110
+ resolve_method_node.arguments.any? do |arg|
111
+ arg.arg_type? || arg.kwrestarg_type? || arg.forward_arg_type?
112
+ end
113
+ end
114
+
112
115
  def register_offense(node, unresolved_args)
113
116
  unresolved_args_source = unresolved_args.map { |v| "#{v}:" }.join(", ")
114
117
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  def_node_matcher :argument_block, <<~PATTERN
11
11
  (block
12
12
  (send nil? :argument ...)
13
- (args)
13
+ (args ...)
14
14
  $...
15
15
  )
16
16
  PATTERN
@@ -20,13 +20,18 @@ module RuboCop
20
20
  module DescriptionMethod
21
21
  extend RuboCop::NodePattern::Macros
22
22
 
23
- def_node_matcher :description_kwarg?, <<~PATTERN
24
- (send nil? :description
25
- {({str|dstr|const} ...)|(send const ...)|(send ({str|dstr} ...) _)})
23
+ DESCRIPTION_STRING = "{({str|dstr|const} ...)|(send const ...)|(send ({str|dstr} ...) _)}"
24
+
25
+ def_node_matcher :description_method_call?, <<~PATTERN
26
+ (send nil? :description #{DESCRIPTION_STRING})
27
+ PATTERN
28
+
29
+ def_node_matcher :description_with_block_arg?, <<~PATTERN
30
+ (send (lvar _) {:description= :description} #{DESCRIPTION_STRING})
26
31
  PATTERN
27
32
 
28
33
  def find_description_method(nodes)
29
- nodes.find { |kwarg| description_kwarg?(kwarg) }
34
+ nodes.find { |kwarg| description_method_call?(kwarg) || description_with_block_arg?(kwarg) }
30
35
  end
31
36
  end
32
37
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  def_node_matcher :field_block, <<~PATTERN
11
11
  (block
12
12
  (send nil? :field ...)
13
- (args)
13
+ (args ...)
14
14
  {(begin $...)|$...}
15
15
  )
16
16
  PATTERN
@@ -6,6 +6,18 @@ module RuboCop
6
6
  extend Forwardable
7
7
  extend RuboCop::NodePattern::Macros
8
8
 
9
+ # These constants were extracted from graphql-ruby in lib/graphql/schema/member/has_fields.rb
10
+ RUBY_KEYWORDS = %i[class module def undef begin rescue ensure end if unless then elsif else
11
+ case when while until for break next redo retry in do return yield super
12
+ self nil true false and or not alias defined? BEGIN END __LINE__
13
+ __FILE__].freeze
14
+
15
+ GRAPHQL_RUBY_KEYWORDS = %i[context object raw_value].freeze
16
+
17
+ CONFLICT_FIELD_NAMES = Set.new(
18
+ GRAPHQL_RUBY_KEYWORDS + RUBY_KEYWORDS + Object.instance_methods
19
+ )
20
+
9
21
  def_delegators :@node, :sibling_index, :parent
10
22
 
11
23
  def_node_matcher :field_name, <<~PATTERN
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ module Heredoc
6
+ def heredoc?(node)
7
+ (node.str_type? || node.dstr_type?) && node.heredoc?
8
+ end
9
+
10
+ def range_including_heredoc(node)
11
+ field = RuboCop::GraphQL::Field.new(node)
12
+ last_heredoc = field.kwargs.instance_variable_get(:@nodes).reverse.find do |kwarg|
13
+ heredoc?(kwarg.value)
14
+ end&.value
15
+
16
+ range = node.loc.expression
17
+ range = range.join(last_heredoc.loc.heredoc_end) if last_heredoc
18
+
19
+ range_by_whole_lines(range)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ module Sorbet
6
+ extend RuboCop::NodePattern::Macros
7
+
8
+ def_node_matcher(:sorbet_signature, <<~PATTERN)
9
+ (block (send nil? :sig) (args) ...)
10
+ PATTERN
11
+
12
+ def has_sorbet_signature?(node)
13
+ !!sorbet_signature_for(node)
14
+ end
15
+
16
+ def sorbet_signature_for(node)
17
+ node.parent.each_descendant.find do |sibling|
18
+ siblings_in_sequence?(sibling, node) &&
19
+ sorbet_signature(sibling)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def siblings_in_sequence?(first_node, second_node)
26
+ first_node.sibling_index - second_node.sibling_index == - 1
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.13.0".freeze
3
+ VERSION = "0.14.2".freeze
4
4
  end
5
5
  end
@@ -8,8 +8,10 @@ require_relative "rubocop/graphql"
8
8
  require_relative "rubocop/graphql/version"
9
9
  require_relative "rubocop/graphql/inject"
10
10
  require_relative "rubocop/graphql/description_method"
11
+ require_relative "rubocop/graphql/heredoc"
11
12
  require_relative "rubocop/graphql/node_pattern"
12
13
  require_relative "rubocop/graphql/node_uniqueness"
14
+ require_relative "rubocop/graphql/sorbet"
13
15
  require_relative "rubocop/graphql/swap_range"
14
16
 
15
17
  require_relative "rubocop/graphql/argument"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Tsepelev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-11 00:00:00.000000000 Z
11
+ date: 2022-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -112,10 +112,12 @@ files:
112
112
  - lib/rubocop/graphql/field.rb
113
113
  - lib/rubocop/graphql/field/block.rb
114
114
  - lib/rubocop/graphql/field/kwargs.rb
115
+ - lib/rubocop/graphql/heredoc.rb
115
116
  - lib/rubocop/graphql/inject.rb
116
117
  - lib/rubocop/graphql/node_pattern.rb
117
118
  - lib/rubocop/graphql/node_uniqueness.rb
118
119
  - lib/rubocop/graphql/schema_member.rb
120
+ - lib/rubocop/graphql/sorbet.rb
119
121
  - lib/rubocop/graphql/swap_range.rb
120
122
  - lib/rubocop/graphql/version.rb
121
123
  homepage: https://github.com/DmitryTsepelev/rubocop-graphql
@@ -125,7 +127,7 @@ metadata:
125
127
  homepage_uri: https://github.com/DmitryTsepelev/rubocop-graphql
126
128
  source_code_uri: https://github.com/DmitryTsepelev/rubocop-graphql
127
129
  changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/blob/master/CHANGELOG.md
128
- post_install_message:
130
+ post_install_message:
129
131
  rdoc_options: []
130
132
  require_paths:
131
133
  - lib
@@ -140,8 +142,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
142
  - !ruby/object:Gem::Version
141
143
  version: '0'
142
144
  requirements: []
143
- rubygems_version: 3.0.3.1
144
- signing_key:
145
+ rubygems_version: 3.2.15
146
+ signing_key:
145
147
  specification_version: 4
146
148
  summary: Automatic performance checking tool for Ruby code.
147
149
  test_files: []