rubocop-graphql 0.11.1 → 0.12.1

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: 660e5888a933adf211a4c140882cd9bab799e0f0bcaa545f7ec1bbe9668e064f
4
- data.tar.gz: efca9c85b37c7a91d46844ef7f574dc434ffffbb1a01adfcefc3a31c71cf2d02
3
+ metadata.gz: 34e03ca837f50f20a65cb0e80d7322c5b2998160148dbc354052875335efee8d
4
+ data.tar.gz: d14bb40f366fa3320c56262a7fafa7d47ffbe445092e481b3d9d0e946bd64649
5
5
  SHA512:
6
- metadata.gz: b51da944eeca0ff3be527823992860e9c690bf8dca86eab7800e0d694cd212654bdc614c5e849516bf7afabbb36c36c1da8cdcb7bb490c84f66606d79439dab5
7
- data.tar.gz: acd228993fe068085136b68e21d872ac52db7ddd7dd36e7ee047d95b2f7fe406db2af8de01a51275b023d45a29bf4a9b0eca60d824fcfb31742e3115b0703bb8
6
+ metadata.gz: 634b1f66850e898eb545d78da1f6ccb8c0b0bc4fe11d5910e9d8f4820ea1af19e5d55a09ca755acfea73eb6cbb114ac59436a1dae6e6282da8b5fc176c09533e
7
+ data.tar.gz: 95ec70c992b7428116555b7c1107d1465d14ce7aa6753efea42ad4739a3c6c4bb3f67f0450eb320cd04d8bba4afbce0b00d1d3e4cef8aa305fa4eb80f81113bd
data/config/default.yml CHANGED
@@ -1,6 +1,10 @@
1
1
  GraphQL:
2
+ Enabled: true
2
3
  Include:
3
4
  - "**/graphql/**/*"
5
+ Exclude:
6
+ - "spec/**/*"
7
+ - "test/**/*"
4
8
 
5
9
  GraphQL/ArgumentDescription:
6
10
  Enabled: true
@@ -101,4 +105,8 @@ GraphQL/OrderedFields:
101
105
  VersionAdded: '0.80'
102
106
  Description: 'Fields should be alphabetically sorted within groups'
103
107
 
108
+ GraphQL/UnusedArgument:
109
+ Enabled: true
110
+ Description: 'Arguments should either be listed explicitly or **rest should be in the resolve signature'
111
+
104
112
 
@@ -24,19 +24,25 @@ module RuboCop
24
24
  "Argument `%<current>s` is duplicated%<field_name>s."
25
25
 
26
26
  def on_class(node)
27
- global_argument_names = Set.new
28
- argument_names_by_field = {}
27
+ return if nested_class?(node)
28
+
29
+ # { "MyClassName" => { "test_field" => <Set: {"field_arg_name"}> } }
30
+ argument_names_by_field_by_class = Hash.new do |h, k|
31
+ h[k] = Hash.new do |h1, k1|
32
+ h1[k1] = Set.new
33
+ end
34
+ end
29
35
 
30
36
  argument_declarations(node).each do |current|
31
37
  current_field_name = field_name(current)
32
38
  current_argument_name = argument_name(current)
39
+ class_name = current_class_name(current)
33
40
 
34
- if current_field_name
35
- argument_names_by_field[current_field_name] ||= Set.new
36
- argument_names = argument_names_by_field[current_field_name]
37
- else
38
- argument_names = global_argument_names
39
- end
41
+ argument_names = if current_field_name
42
+ argument_names_by_field_by_class[class_name][current_field_name]
43
+ else
44
+ argument_names_by_field_by_class[class_name][:root]
45
+ end
40
46
 
41
47
  unless argument_names.include?(current_argument_name)
42
48
  argument_names.add(current_argument_name)
@@ -49,6 +55,14 @@ module RuboCop
49
55
 
50
56
  private
51
57
 
58
+ def current_class_name(node)
59
+ node.each_ancestor(:class).first.defined_module_name
60
+ end
61
+
62
+ def nested_class?(node)
63
+ node.each_ancestor(:class).any?
64
+ end
65
+
52
66
  def register_offense(current)
53
67
  current_field_name = field_name(current)
54
68
  field_name_message = " in field `#{current_field_name}`" if current_field_name
@@ -31,9 +31,13 @@ module RuboCop
31
31
  "Field `%<current>s` is duplicated."
32
32
 
33
33
  def on_class(node)
34
- field_names = Set.new
34
+ return if nested_class?(node)
35
+
36
+ field_names_by_class = Hash.new { |h, k| h[k] = Set.new }
35
37
 
36
38
  field_declarations(node).each do |current|
39
+ current_class = current_class_name(current)
40
+ field_names = field_names_by_class[current_class]
37
41
  current_field_name = field_name(current)
38
42
 
39
43
  unless field_names.include?(current_field_name)
@@ -47,6 +51,10 @@ module RuboCop
47
51
 
48
52
  private
49
53
 
54
+ def nested_class?(node)
55
+ node.each_ancestor(:class).any?
56
+ end
57
+
50
58
  def register_offense(current)
51
59
  message = format(
52
60
  self.class::MSG,
@@ -60,6 +68,10 @@ module RuboCop
60
68
  node.first_argument.value.to_s
61
69
  end
62
70
 
71
+ def current_class_name(node)
72
+ node.each_ancestor(:class).first.defined_module_name
73
+ end
74
+
63
75
  def_node_search :field_declarations, <<~PATTERN
64
76
  {
65
77
  (send nil? :field (:sym _) ...)
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module GraphQL
6
+ # Arguments should either be listed explicitly or **rest should be in the resolve signature.
7
+ #
8
+ # @example
9
+ # # good
10
+ #
11
+ # class SomeResolver < Resolvers::Base
12
+ # argument :arg1, String, required: true
13
+ # argument :user_id, String, required: true, loads: Types::UserType
14
+ # argument :post_id, String, loads: Types::PostType, as: :article
15
+ # argument :comment_ids, String, loads: Types::CommentType
16
+ #
17
+ # def resolve(arg1:, user:, article:, comments:); end
18
+ # end
19
+ #
20
+ # # good
21
+ #
22
+ # class SomeResolver < Resolvers::Base
23
+ # argument :arg1, String, required: true
24
+ # argument :user_id, String, required: true, loads: Types::UserType
25
+ # argument :comment_ids, String, loads: Types::CommentType
26
+ #
27
+ # def resolve(arg1:, **rest); end
28
+ # end
29
+ #
30
+ # # good
31
+ #
32
+ # class SomeResolver < Resolvers::Base
33
+ # type SomeType, null: false
34
+ #
35
+ # argument :arg1, String, required: true
36
+ # argument :arg2, String, required: true
37
+ #
38
+ # def resolve(args); end
39
+ # end
40
+ #
41
+ # # bad
42
+ #
43
+ # class SomeResolver < Resolvers::Base
44
+ # type SomeType, null: false
45
+ #
46
+ # argument :arg1, String, required: true
47
+ # argument :arg2, String, required: true
48
+ #
49
+ # def resolve(arg1:); end
50
+ # end
51
+ #
52
+ # # bad
53
+ #
54
+ # class SomeResolver < Resolvers::Base
55
+ # type SomeType, null: false
56
+ #
57
+ # argument :arg1, String, required: true
58
+ # argument :arg2, String, required: true
59
+ #
60
+ # def resolve; end
61
+ # end
62
+ #
63
+ class UnusedArgument < Base
64
+ extend AutoCorrector
65
+
66
+ MSG = "Argument%<ending>s `%<unresolved_args>s` should be listed in the resolve signature."
67
+
68
+ def on_class(node)
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
74
+
75
+ declared_arg_nodes = argument_declarations(node)
76
+ return unless declared_arg_nodes.any?
77
+
78
+ unresolved_args = find_unresolved_args(resolve_method_node, declared_arg_nodes)
79
+ register_offense(resolve_method_node, unresolved_args) if unresolved_args.any?
80
+ end
81
+
82
+ private
83
+
84
+ def find_resolve_method_node(node)
85
+ resolve_method_nodes = resolve_method_definition(node)
86
+ resolve_method_nodes.to_a.last if resolve_method_nodes.any?
87
+ end
88
+
89
+ def find_unresolved_args(method_node, declared_arg_nodes)
90
+ resolve_method_kwargs = method_node.arguments.select do |arg|
91
+ arg.kwarg_type? || arg.kwoptarg_type?
92
+ end
93
+ resolve_method_kwargs_names = resolve_method_kwargs.map do |node|
94
+ node.node_parts[0]
95
+ end.to_set
96
+ declared_args = declared_arg_nodes.map { |node| RuboCop::GraphQL::Argument.new(node) }
97
+ declared_args.map(&method(:arg_name)).uniq.reject do |declared_arg_name|
98
+ resolve_method_kwargs_names.include?(declared_arg_name)
99
+ end
100
+ end
101
+
102
+ def register_offense(node, unresolved_args)
103
+ unresolved_args_source = unresolved_args.map { |v| "#{v}:" }.join(", ")
104
+
105
+ message = format(
106
+ self.class::MSG,
107
+ unresolved_args: unresolved_args_source,
108
+ ending: unresolved_args.size == 1 ? "" : "s"
109
+ )
110
+
111
+ add_offense(node, message: message) do |corrector|
112
+ if node.arguments?
113
+ corrector.insert_after(arg_end(node.arguments.last), ", #{unresolved_args_source}")
114
+ else
115
+ corrector.insert_after(method_name(node), "(#{unresolved_args_source})")
116
+ end
117
+ end
118
+ end
119
+
120
+ def method_name(node)
121
+ node.loc.keyword.end.resize(node.method_name.to_s.size + 1)
122
+ end
123
+
124
+ def arg_end(node)
125
+ node.loc.expression.end
126
+ end
127
+
128
+ def inferred_arg_name(name_as_string)
129
+ case name_as_string
130
+ when /_id$/
131
+ name_as_string.sub(/_id$/, "").to_sym
132
+ when /_ids$/
133
+ name_as_string.sub(/_ids$/, "")
134
+ .sub(/([^s])$/, "\\1s")
135
+ .to_sym
136
+ else
137
+ name
138
+ end
139
+ end
140
+
141
+ def arg_name(declared_arg)
142
+ if declared_arg.kwargs.loads.nil?
143
+ declared_arg.name
144
+ else
145
+ declared_arg.as || inferred_arg_name(declared_arg.name.to_s)
146
+ end
147
+ end
148
+
149
+ def_node_search :argument_declarations, <<~PATTERN
150
+ (send nil? :argument (:sym _) ...)
151
+ PATTERN
152
+
153
+ def_node_search :resolve_method_definition, <<~PATTERN
154
+ (def :resolve
155
+ (args ...) ...)
156
+ PATTERN
157
+ end
158
+ end
159
+ end
160
+ end
@@ -16,3 +16,4 @@ require_relative "graphql/resolver_method_length"
16
16
  require_relative "graphql/object_description"
17
17
  require_relative "graphql/ordered_arguments"
18
18
  require_relative "graphql/ordered_fields"
19
+ require_relative "graphql/unused_argument"
@@ -19,6 +19,14 @@ module RuboCop
19
19
  (pair (sym :description) ...)
20
20
  PATTERN
21
21
 
22
+ def_node_matcher :loads_kwarg?, <<~PATTERN
23
+ (pair (sym :loads) ...)
24
+ PATTERN
25
+
26
+ def_node_matcher :as_kwarg?, <<~PATTERN
27
+ (pair (sym :as) ...)
28
+ PATTERN
29
+
22
30
  def initialize(argument_node)
23
31
  @nodes = argument_kwargs(argument_node) || []
24
32
  end
@@ -26,6 +34,14 @@ module RuboCop
26
34
  def description
27
35
  @nodes.find { |kwarg| description_kwarg?(kwarg) }
28
36
  end
37
+
38
+ def loads
39
+ @nodes.find { |kwarg| loads_kwarg?(kwarg) }
40
+ end
41
+
42
+ def as
43
+ @nodes.find { |kwarg| as_kwarg?(kwarg) }
44
+ end
29
45
  end
30
46
  end
31
47
  end
@@ -13,6 +13,10 @@ module RuboCop
13
13
  (send nil? :argument (:sym $_) ...)
14
14
  PATTERN
15
15
 
16
+ def_node_matcher :argument_as, <<~PATTERN
17
+ (pair (sym :as) (sym $_))
18
+ PATTERN
19
+
16
20
  attr_reader :node
17
21
 
18
22
  def initialize(node)
@@ -23,6 +27,10 @@ module RuboCop
23
27
  @name ||= argument_name(@node)
24
28
  end
25
29
 
30
+ def as
31
+ @as ||= argument_as(kwargs.as)
32
+ end
33
+
26
34
  def description
27
35
  @description ||= argument_description(@node) || kwargs.description || block.description
28
36
  end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.11.1".freeze
3
+ VERSION = "0.12.1".freeze
4
4
  end
5
5
  end
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.11.1
4
+ version: 0.12.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: 2021-12-01 00:00:00.000000000 Z
11
+ date: 2022-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,6 +100,7 @@ files:
100
100
  - lib/rubocop/cop/graphql/ordered_arguments.rb
101
101
  - lib/rubocop/cop/graphql/ordered_fields.rb
102
102
  - lib/rubocop/cop/graphql/resolver_method_length.rb
103
+ - lib/rubocop/cop/graphql/unused_argument.rb
103
104
  - lib/rubocop/cop/graphql_cops.rb
104
105
  - lib/rubocop/graphql.rb
105
106
  - lib/rubocop/graphql/argument.rb
@@ -121,8 +122,8 @@ licenses:
121
122
  metadata:
122
123
  homepage_uri: https://github.com/DmitryTsepelev/rubocop-graphql
123
124
  source_code_uri: https://github.com/DmitryTsepelev/rubocop-graphql
124
- changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/CHANGELOG.md
125
- post_install_message:
125
+ changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/blob/master/CHANGELOG.md
126
+ post_install_message:
126
127
  rdoc_options: []
127
128
  require_paths:
128
129
  - lib
@@ -137,8 +138,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
138
  - !ruby/object:Gem::Version
138
139
  version: '0'
139
140
  requirements: []
140
- rubygems_version: 3.2.31
141
- signing_key:
141
+ rubygems_version: 3.0.3.1
142
+ signing_key:
142
143
  specification_version: 4
143
144
  summary: Automatic performance checking tool for Ruby code.
144
145
  test_files: []