rubocop-graphql 0.11.1 → 0.12.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: 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: []