Class: RuboCop::Cop::Gusto::Graphql::ResolverIgnoresObject
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Gusto::Graphql::ResolverIgnoresObject
- Defined in:
- lib/rubocop/cop/gusto/graphql/resolver_ignores_object.rb
Overview
Flags a field whose resolver cannot reach the node it hangs off: the method body never
reads object or @object, and no helper it calls in the same class does either. The
value is then identical for every node, so the field does not belong on this type.
Constant Summary collapse
- MSG =
"Field `:%{name}` has a resolver that never reads `object`, so its value is the same " \ "for every node. Re-home it, or, if it really does depend on the node, resolve the " \ "indirect dependency."
- FIELD_NAME_NODE =
RuboCop::AST::NodePattern.new("(send nil? :field $(sym _) ...)")
- RESOLUTION_ELSEWHERE =
RuboCop::AST::NodePattern.new("(send nil? :field ... (hash <(pair (sym {:resolver :hash_key}) _) ...>))\n")
- RESOLVER_METHOD =
resolver_method:redirects the lookup on the type instance;method:names a method on the backing object, which this cop does not read. RuboCop::AST::NodePattern.new("(send nil? :field ... (hash <(pair (sym :resolver_method) (sym $_)) ...>))\n")
- EPHEMERAL_BACKING =
RuboCop::AST::NodePattern.new("(send nil? :set_backing_type (const ... :EphemeralObject))\n")
- DELEGATE_TO_OBJECT =
RuboCop::AST::NodePattern.new("(send nil? :delegate ... (hash <(pair (sym :to) (sym :object)) ...>))\n")
- NODE_READ_THROUGH_RECEIVER =
A concern that
requires_ancestorreaches the node throughT.bind(self, ...).object, since Sorbet cannot seeobjecton the module itself. That reads the node exactly as a bareobjectdoes, so the receiver form has to count too. RuboCop::AST::NodePattern.new("(send {self (send (const _ :T) :bind self ...)} :object)\n")
- SCOPE_BOUNDARY =
A nested class gets its own
on_class, so the walk must not cross into one. i(class module sclass).freeze
- BODY_BOUNDARY =
Fields are never declared inside a
def, so stopping there loses nothing. i(class module sclass def).freeze
Instance Method Summary collapse
- #on_class(node) ⇒ Object (also: #on_module)
Instance Method Details
#on_class(node) ⇒ Object Also known as: on_module
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/rubocop/cop/gusto/graphql/resolver_ignores_object.rb', line 61 def on_class(node) return unless node.body fields = [] definitions = [] delegated = [] ephemeral = T.let(false, T::Boolean) walk(node.body, BODY_BOUNDARY) do |child| case child.type when :send next unless child.receiver.nil? case child.method_name when :field name_node = FIELD_NAME_NODE.match(child) next if name_node.nil? || RESOLUTION_ELSEWHERE.match(child) fields << [name_node, RESOLVER_METHOD.match(child) || name_node.value] when :set_backing_type ephemeral = true if EPHEMERAL_BACKING.match(child) when :delegate delegated.concat(delegated_names(child)) end when :def definitions << child end end return if ephemeral || fields.empty? accessors = node_accessors | delegated resolvers = {} direct = [] callers_of = {} definitions.each { |d| scan_resolver(d, accessors, resolvers, direct, callers_of) } reaching = propagate(direct, callers_of) fields.each do |name_node, resolver_name| next unless resolvers.key?(resolver_name) next if reaching.key?(resolver_name) add_offense(name_node, message: format(MSG, name: name_node.value)) end end |