rubocop-graphql 0.12.3 → 0.14.1

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: b0b5a6defb854d907bf12c23a7262adc13b55228e3d7cf345f19ea7b1371bacb
4
- data.tar.gz: 3250274062d10447eb23994e4bc1e99393d4d03a6b17221cdbda5f1d1bd4b2d6
3
+ metadata.gz: 99da3dd6f8e99724cb14b723f2f4ed2c7149dd2921b1295e4a669697b1eed8d2
4
+ data.tar.gz: 964c04401fb03c769213c7ccd3142e8969b83a741e8325b5916ee91c9daea11d
5
5
  SHA512:
6
- metadata.gz: 3f59543ad00f27cf92048d5fa3fb7e09ea99c9e1179839f848316025eb48d66a92012f2cdb13847113d602b740a49aab128d8a4538cf5d8c0f8ea8a2c0f47321
7
- data.tar.gz: 2b5110aa616044c726f466c6e82a36af0cdad58d096c6359a97a91b3d0c6e023dd4e2ce96f8ecaf44638393bce6b2a4330fa2b3ff76b8c5ecc89d0b07ed6f5e4
6
+ metadata.gz: d284c67aec9969f151437f45751978f8d26da471484b816adcb633f31d8e55950ef09d8035d686382cc44c4eae8c895192dbdbfcf9652cb1cd2594961fbc295c
7
+ data.tar.gz: af01d1a6e41a303bafa730578bb187462ce4f8ecf5c2436544c68192eceacc99d99c1d01c4cd1cff235e53341ddf9dd1d298f75b2f5f565fd0bd0a3cfc7ba01a
@@ -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)
@@ -57,7 +57,19 @@ module RuboCop
57
57
  "Field `%<current>s` should appear before `%<previous>s`."
58
58
 
59
59
  def on_class(node)
60
- argument_declarations(node).each_cons(2) do |previous, current|
60
+ declarations_with_blocks = argument_declarations_with_blocks(node)
61
+ declarations_without_blocks = argument_declarations_without_blocks(node)
62
+
63
+ argument_declarations = declarations_without_blocks.map do |node|
64
+ arg_name = argument_name(node)
65
+ same_arg_with_block_declaration = declarations_with_blocks.find do |dec|
66
+ argument_name(dec) == arg_name
67
+ end
68
+
69
+ same_arg_with_block_declaration || node
70
+ end
71
+
72
+ argument_declarations.each_cons(2) do |previous, current|
61
73
  next unless consecutive_lines(previous, current)
62
74
  next if argument_name(current) >= argument_name(previous)
63
75
 
@@ -80,16 +92,26 @@ module RuboCop
80
92
  end
81
93
 
82
94
  def argument_name(node)
83
- node.first_argument.value.to_s
95
+ argument = node.block_type? ? node.children.first.first_argument : node.first_argument
96
+
97
+ argument.value.to_s
84
98
  end
85
99
 
86
100
  def consecutive_lines(previous, current)
87
101
  previous.source_range.last_line == current.source_range.first_line - 1
88
102
  end
89
103
 
90
- def_node_search :argument_declarations, <<~PATTERN
104
+ def_node_search :argument_declarations_without_blocks, <<~PATTERN
91
105
  (send nil? :argument (:sym _) ...)
92
106
  PATTERN
107
+
108
+ def_node_search :argument_declarations_with_blocks, <<~PATTERN
109
+ (block
110
+ (send nil? :argument
111
+ (:sym _)
112
+ ...)
113
+ ...)
114
+ PATTERN
93
115
  end
94
116
  end
95
117
  end
@@ -90,7 +90,10 @@ module RuboCop
90
90
 
91
91
  def find_resolve_method_node(node)
92
92
  resolve_method_nodes = resolve_method_definition(node)
93
- resolve_method_nodes.to_a.last if resolve_method_nodes.any?
93
+ resolve_method_nodes.find do |resolve_node|
94
+ # reject resolve methods from other classes
95
+ resolve_node.each_ancestor(:class).first == node
96
+ end
94
97
  end
95
98
 
96
99
  def find_unresolved_args(method_node, declared_arg_nodes)
@@ -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.12.3".freeze
3
+ VERSION = "0.14.1".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.12.3
4
+ version: 0.14.1
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-01-08 00:00:00.000000000 Z
11
+ date: 2022-03-25 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: []