rubocop-graphql 0.12.2 → 0.14.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: 1d488e2bcf0fac1f61555afb433e26834bc653b88598ba9fd23d58be29db44cc
4
- data.tar.gz: 31afb3a28f7cd44f165dcd4bd93aecebf270ccc2af05f50816f171f19c88680d
3
+ metadata.gz: bf7d97ba00fa1e49f43e15e6dd5ee134169eedff3603dc2fcad6d85d832529f0
4
+ data.tar.gz: ea8b222d69f599a1d9b341fadc15306f8a2b2aff17acf2853aa6129a45bdc056
5
5
  SHA512:
6
- metadata.gz: a5d35e978439e99de72eb4d90088b6a726bfbf78a6303ce422bfb09a05b4d965e7a28acb06ada12538dea8148f691384a48682ad651cc39d631e779d41098cfb
7
- data.tar.gz: 44ca6c9891b4184b6ab40b1a4be8d9398fb94276502a603f09639bf5993c553aee01f845e61a119470f1a7eb5f51659db7114bf47a472c41394649b91fc20c28
6
+ metadata.gz: dc4926760bca79b7a6ea7f3cb73b3b035d52b7cdc2a3819a76aefaadf28b11aef232ee6fed961301cdc65467592effa0d02217d6fdbf8c91bf703688b5834f31
7
+ data.tar.gz: 6b3bb0304272b6c6ec0b793f698f9139d53da73bbdf2038ab26d4734c754f5b098e075fcf354c361c71f1e82bdaf3be358be1c9b85065d631e5e54fa89b978a1
@@ -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
 
@@ -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
@@ -72,7 +72,7 @@ module RuboCop
72
72
  arg.arg_type? || arg.kwrestarg_type?
73
73
  end
74
74
 
75
- declared_arg_nodes = argument_declarations(node)
75
+ declared_arg_nodes = find_declared_arg_nodes(node)
76
76
  return unless declared_arg_nodes.any?
77
77
 
78
78
  unresolved_args = find_unresolved_args(resolve_method_node, declared_arg_nodes)
@@ -81,9 +81,19 @@ module RuboCop
81
81
 
82
82
  private
83
83
 
84
+ def find_declared_arg_nodes(node)
85
+ argument_declarations(node).select do |arg_declaration|
86
+ # argument is declared on the same class that is being analyzed
87
+ arg_declaration.each_ancestor(:class).first == node
88
+ end
89
+ end
90
+
84
91
  def find_resolve_method_node(node)
85
92
  resolve_method_nodes = resolve_method_definition(node)
86
- 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
87
97
  end
88
98
 
89
99
  def find_unresolved_args(method_node, declared_arg_nodes)
@@ -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
@@ -7,10 +7,6 @@ module RuboCop
7
7
  def current_class_full_name(node)
8
8
  node.each_ancestor(:class).map(&:defined_module_name).join("::")
9
9
  end
10
-
11
- def nested_class?(node)
12
- node.each_ancestor(:class).any?
13
- end
14
10
  end
15
11
  end
16
12
  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.2".freeze
3
+ VERSION = "0.14.0".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.2
4
+ version: 0.14.0
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-07 00:00:00.000000000 Z
11
+ date: 2022-03-23 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: []