rubocop-graphql 0.11.2 → 0.12.2

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: fa0ffd49d0fa9883050aa121e7a344afff34060a89b5d995023dcb76437e8923
4
- data.tar.gz: f34f5c5ddfedd3fb597b0216c88d56cf554bb124e14633a8632f8045c6e935be
3
+ metadata.gz: 1d488e2bcf0fac1f61555afb433e26834bc653b88598ba9fd23d58be29db44cc
4
+ data.tar.gz: 31afb3a28f7cd44f165dcd4bd93aecebf270ccc2af05f50816f171f19c88680d
5
5
  SHA512:
6
- metadata.gz: 00efa45967096346f711c41c712d7e1c09cc301e81c713f9dc7359120854d5273312e471de989d80fbaa5a8f3fa1736dd3b3de4e18688c1f06ed3b86b68dcede
7
- data.tar.gz: a35bfa43b6a7acf8d2820f7e91f442c0ab04ab9867db0e43d427c90daff711ccf47080b66b62f122fbca416e6c604f69e591f4124c24fdf54304f78f5e1d1dd8
6
+ metadata.gz: a5d35e978439e99de72eb4d90088b6a726bfbf78a6303ce422bfb09a05b4d965e7a28acb06ada12538dea8148f691384a48682ad651cc39d631e779d41098cfb
7
+ data.tar.gz: 44ca6c9891b4184b6ab40b1a4be8d9398fb94276502a603f09639bf5993c553aee01f845e61a119470f1a7eb5f51659db7114bf47a472c41394649b91fc20c28
data/config/default.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  GraphQL:
2
+ Enabled: true
2
3
  Include:
3
4
  - "**/graphql/**/*"
4
5
  Exclude:
@@ -104,4 +105,8 @@ GraphQL/OrderedFields:
104
105
  VersionAdded: '0.80'
105
106
  Description: 'Fields should be alphabetically sorted within groups'
106
107
 
108
+ GraphQL/UnusedArgument:
109
+ Enabled: true
110
+ Description: 'Arguments should either be listed explicitly or **rest should be in the resolve signature'
111
+
107
112
 
@@ -20,23 +20,31 @@ module RuboCop
20
20
  # end
21
21
  #
22
22
  class ArgumentUniqueness < Base
23
+ include RuboCop::GraphQL::NodeUniqueness
24
+
23
25
  MSG = "Argument names should only be defined once per block. "\
24
26
  "Argument `%<current>s` is duplicated%<field_name>s."
25
27
 
26
28
  def on_class(node)
27
- global_argument_names = Set.new
28
- argument_names_by_field = {}
29
+ return if ::RuboCop::GraphQL::Class.new(node).nested?
30
+
31
+ # { "MyClassName" => { "test_field" => <Set: {"field_arg_name"}> } }
32
+ argument_names_by_field_by_class = Hash.new do |h, k|
33
+ h[k] = Hash.new do |h1, k1|
34
+ h1[k1] = Set.new
35
+ end
36
+ end
29
37
 
30
38
  argument_declarations(node).each do |current|
31
39
  current_field_name = field_name(current)
32
40
  current_argument_name = argument_name(current)
41
+ class_name = current_class_full_name(current)
33
42
 
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
43
+ argument_names = if current_field_name
44
+ argument_names_by_field_by_class[class_name][current_field_name]
45
+ else
46
+ argument_names_by_field_by_class[class_name][:root]
47
+ end
40
48
 
41
49
  unless argument_names.include?(current_argument_name)
42
50
  argument_names.add(current_argument_name)
@@ -27,13 +27,19 @@ module RuboCop
27
27
  # end
28
28
  #
29
29
  class FieldUniqueness < Base
30
+ include RuboCop::GraphQL::NodeUniqueness
31
+
30
32
  MSG = "Field names should only be defined once per type. "\
31
33
  "Field `%<current>s` is duplicated."
32
34
 
33
35
  def on_class(node)
34
- field_names = Set.new
36
+ return if ::RuboCop::GraphQL::Class.new(node).nested?
37
+
38
+ field_names_by_class = Hash.new { |h, k| h[k] = Set.new }
35
39
 
36
40
  field_declarations(node).each do |current|
41
+ current_class = current_class_full_name(current)
42
+ field_names = field_names_by_class[current_class]
37
43
  current_field_name = field_name(current)
38
44
 
39
45
  unless field_names.include?(current_field_name)
@@ -0,0 +1,159 @@
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
+ return declared_arg.as if declared_arg.kwargs.as
143
+ return inferred_arg_name(declared_arg.name.to_s) if declared_arg.kwargs.loads
144
+
145
+ declared_arg.name
146
+ end
147
+
148
+ def_node_search :argument_declarations, <<~PATTERN
149
+ (send nil? :argument (:sym _) ...)
150
+ PATTERN
151
+
152
+ def_node_search :resolve_method_definition, <<~PATTERN
153
+ (def :resolve
154
+ (args ...) ...)
155
+ PATTERN
156
+ end
157
+ end
158
+ end
159
+ 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
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ class Class
6
+ attr_reader :node
7
+
8
+ def initialize(node)
9
+ @node = node
10
+ end
11
+
12
+ def nested?
13
+ node.each_ancestor(:class).any?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module GraphQL
5
+ # Shared methods to check duplicated definitions
6
+ module NodeUniqueness
7
+ def current_class_full_name(node)
8
+ node.each_ancestor(:class).map(&:defined_module_name).join("::")
9
+ end
10
+
11
+ def nested_class?(node)
12
+ node.each_ancestor(:class).any?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module GraphQL
3
- VERSION = "0.11.2".freeze
3
+ VERSION = "0.12.2".freeze
4
4
  end
5
5
  end
@@ -9,11 +9,13 @@ require_relative "rubocop/graphql/version"
9
9
  require_relative "rubocop/graphql/inject"
10
10
  require_relative "rubocop/graphql/description_method"
11
11
  require_relative "rubocop/graphql/node_pattern"
12
+ require_relative "rubocop/graphql/node_uniqueness"
12
13
  require_relative "rubocop/graphql/swap_range"
13
14
 
14
15
  require_relative "rubocop/graphql/argument"
15
16
  require_relative "rubocop/graphql/argument/block"
16
17
  require_relative "rubocop/graphql/argument/kwargs"
18
+ require_relative "rubocop/graphql/class"
17
19
  require_relative "rubocop/graphql/field"
18
20
  require_relative "rubocop/graphql/field/block"
19
21
  require_relative "rubocop/graphql/field/kwargs"
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.2
4
+ version: 0.12.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: 2021-12-03 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,11 +100,13 @@ 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
106
107
  - lib/rubocop/graphql/argument/block.rb
107
108
  - lib/rubocop/graphql/argument/kwargs.rb
109
+ - lib/rubocop/graphql/class.rb
108
110
  - lib/rubocop/graphql/description_method.rb
109
111
  - lib/rubocop/graphql/ext/snake_case.rb
110
112
  - lib/rubocop/graphql/field.rb
@@ -112,6 +114,7 @@ files:
112
114
  - lib/rubocop/graphql/field/kwargs.rb
113
115
  - lib/rubocop/graphql/inject.rb
114
116
  - lib/rubocop/graphql/node_pattern.rb
117
+ - lib/rubocop/graphql/node_uniqueness.rb
115
118
  - lib/rubocop/graphql/schema_member.rb
116
119
  - lib/rubocop/graphql/swap_range.rb
117
120
  - lib/rubocop/graphql/version.rb
@@ -121,8 +124,8 @@ licenses:
121
124
  metadata:
122
125
  homepage_uri: https://github.com/DmitryTsepelev/rubocop-graphql
123
126
  source_code_uri: https://github.com/DmitryTsepelev/rubocop-graphql
124
- changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/CHANGELOG.md
125
- post_install_message:
127
+ changelog_uri: https://github.com/DmitryTsepelev/rubocop-graphql/blob/master/CHANGELOG.md
128
+ post_install_message:
126
129
  rdoc_options: []
127
130
  require_paths:
128
131
  - lib
@@ -137,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
140
  - !ruby/object:Gem::Version
138
141
  version: '0'
139
142
  requirements: []
140
- rubygems_version: 3.2.31
141
- signing_key:
143
+ rubygems_version: 3.0.3.1
144
+ signing_key:
142
145
  specification_version: 4
143
146
  summary: Automatic performance checking tool for Ruby code.
144
147
  test_files: []