rubocop-require_tools 0.2.0 → 0.3.0

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: 92a502fba8cea8224ca73240fca61774a3a3c7dd9a572ba902da7cd5cdce804a
4
- data.tar.gz: 7dcd0456375b56dfeaff84693ee49988e0b59efcb228622fbab249704645b99c
3
+ metadata.gz: 904921f72984631692f9d38ce8f6e6ba4ebc99383c871bb09bb518c419336f60
4
+ data.tar.gz: d6838acd03ecd1c02de7baa0c62ff2ed66944d3d82b8f4127c594e2215682534
5
5
  SHA512:
6
- metadata.gz: abcd24dd207443eeba31b9a7ab62444dea88c2df9bf619980136622570bc1eaa6aa935f5aa0cf1a220519f6fa105e21ced27f6510dd78521ce2edfad020444b5
7
- data.tar.gz: 1c5570d3ff8bc845f313d9825a4d6b63f03b1fdfccd3b6cca0ee7a16b7cb9cea4a4f4882c2f65a7841348841f377c93c0fc76cd8ae4a42bd169770ea68f7effa
6
+ metadata.gz: ac6e57ced1eda99834806160ff201a5dc2c5beaf0f139143ed5ac8db628efe7f66e2d6adafeb70b79be827f8217690edda25d96f49f8afee6c4223e84abbd0c7
7
+ data.tar.gz: 24b98cecaa56dd3992a09be447a686291f65960caa1a88a6d0736c03c29dd7224e04442e2a688bea5776326d967d8b02f9270a56abdac33b33fd57f2c730f1ff
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-require_tools (0.2.0)
5
- rubocop (>= 0.49.1)
4
+ rubocop-require_tools (0.3.0)
5
+ rubocop (>= 1.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -11,7 +11,7 @@ GEM
11
11
  coderay (1.1.3)
12
12
  diff-lcs (1.6.2)
13
13
  io-console (0.8.2)
14
- json (2.19.5)
14
+ json (2.19.9)
15
15
  language_server-protocol (3.17.0.5)
16
16
  lint_roller (1.1.0)
17
17
  method_source (1.1.0)
@@ -78,7 +78,7 @@ CHECKSUMS
78
78
  coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
79
79
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
80
80
  io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
81
- json (2.19.5) sha256=218a18553e4801d579ca7e0f5bc72bafd776d7397238a1fb4e74db5b0a812c59
81
+ json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
82
82
  language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
83
83
  lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
84
84
  method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
@@ -98,7 +98,7 @@ CHECKSUMS
98
98
  rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
99
99
  rubocop (1.86.2) sha256=bb2e97f635eda42c448f2588f4a6ff78f221b8bdfdf65b1e9b07fbd57521b45d
100
100
  rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
101
- rubocop-require_tools (0.2.0)
101
+ rubocop-require_tools (0.3.0)
102
102
  ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
103
103
  unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
104
104
  unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # require 'faraday'
17
17
  #
18
18
  # Faraday.new
19
- class MissingRequireStatement < Cop
19
+ class MissingRequireStatement < Base
20
20
  MSG = '`%<constant>s` not found, you\'re probably missing a require statement or there is a cycle in your dependencies.'.freeze
21
21
 
22
22
  attr_writer :timeline
@@ -26,7 +26,7 @@ module RuboCop
26
26
  end
27
27
 
28
28
  # Builds
29
- def investigate(processed_source)
29
+ def on_new_investigation
30
30
  processing_methods = self.methods.select { |m| m.to_s.start_with? 'process_' }
31
31
 
32
32
  stack = [processed_source.ast]
@@ -63,19 +63,6 @@ module RuboCop
63
63
  end
64
64
  end
65
65
 
66
- def add_offense(node, location: nil, message:)
67
- # Work around breaking API changes between rubocop 0.49.1 and later (...)
68
- signature_old = %i[node loc message severity]
69
- param_info = RuboCop::Cop::Cop.instance_method(:add_offense).parameters
70
- if param_info.map(&:last) == signature_old
71
- super(node, location || :expression, message)
72
- elsif location
73
- super(node, location: location, message: message)
74
- else
75
- super(node, message: message)
76
- end
77
- end
78
-
79
66
  def_node_matcher :extract_inner_const, <<-PATTERN
80
67
  (const $!nil? _)
81
68
  PATTERN
@@ -102,11 +89,47 @@ module RuboCop
102
89
  return unless consts
103
90
  const_name = consts.join('::')
104
91
 
105
- self.timeline << { event: :const_access, name: const_name, node: node }
92
+ self.timeline << { event: :const_access, name: const_name, node: node } unless guarded?(node, const_name)
106
93
 
107
94
  { skip: node.children }
108
95
  end
109
96
 
97
+ def_node_matcher :const_defined_check, <<-PATTERN
98
+ (send $_ :const_defined? ({str sym} $_) ...)
99
+ PATTERN
100
+
101
+ # True where a check that this very constant is defined has just passed: in the true branch of
102
+ # an `if`, or on the right of an `&&`. Code that tests for an optional dependency is not missing a
103
+ # require. A check on Foo does not cover Foo::Bar, which may still not be loaded.
104
+ def guarded?(node, const_name)
105
+ child = node
106
+ node.each_ancestor do |ancestor|
107
+ return true if ancestor.type == :defined? # defined?(Foo) is itself the check, and never raises
108
+
109
+ condition = ancestor.children[0] if (ancestor.if_type? || ancestor.and_type?) && child.equal?(ancestor.children[1])
110
+ return true if condition && checked_constants(condition).include?(const_name)
111
+
112
+ child = ancestor
113
+ end
114
+ false
115
+ end
116
+
117
+ def checked_constants(condition)
118
+ return condition.children.flat_map { |c| checked_constants(c) } if condition.and_type?
119
+
120
+ if (checked = const_defined_check(condition))
121
+ receiver, name = checked
122
+ return [] unless receiver&.const_type?
123
+
124
+ path = find_consts(receiver).join('::')
125
+ return [path == 'Object' ? name.to_s : "#{path}::#{name}"]
126
+ end
127
+
128
+ # `defined?` cannot be written as a node pattern: the pattern language reads it as a predicate
129
+ checked = condition.children.first if condition.type == :defined?
130
+ checked&.const_type? ? [find_consts(checked).join('::')] : []
131
+ end
132
+
110
133
  def_node_matcher :extract_const_assignment, <<-PATTERN
111
134
  (casgn nil? $_ ...)
112
135
  PATTERN
@@ -164,6 +187,20 @@ module RuboCop
164
187
  { skip: skip_list, push: push_list }
165
188
  end
166
189
 
190
+ def_node_matcher :extract_include, <<-PATTERN
191
+ (send nil? :include $(const ...))
192
+ PATTERN
193
+
194
+ # `include Foo` makes Foo's constants reachable unqualified in the enclosing class or module.
195
+ def process_include(node, _source)
196
+ return unless node.kind_of? RuboCop::AST::Node
197
+ included = extract_include(node)
198
+ return unless included
199
+
200
+ self.timeline << { event: :include, name: find_consts(included).join('::') }
201
+ nil
202
+ end
203
+
167
204
  def_node_matcher :extract_require, <<-PATTERN
168
205
  (send nil? ${:require :require_relative} (str $_))
169
206
  PATTERN
@@ -205,6 +242,8 @@ module RuboCop
205
242
  err_indices = err_indices.reject { |e| outdated.include?(timeline[e]) }
206
243
  when :const_undef
207
244
  state.undefine_const(const_name: event[:name])
245
+ when :include
246
+ state.include_module(name: event[:name])
208
247
  when :const_assign
209
248
  state.const_assigned(const_name: event[:name], alias_target: event[:alias_target])
210
249
 
@@ -5,11 +5,13 @@ module RuboCop
5
5
  attr_accessor :defined_constants
6
6
  attr_accessor :const_stack
7
7
  attr_accessor :aliases
8
+ attr_accessor :included_modules
8
9
 
9
10
  def initialize
10
11
  self.defined_constants = []
11
12
  self.const_stack = []
12
13
  self.aliases = {}
14
+ self.included_modules = [] # [nesting depth, module name]
13
15
  end
14
16
 
15
17
  def require(file: nil)
@@ -56,6 +58,13 @@ module RuboCop
56
58
 
57
59
  def undefine_const(const_name: nil) # rubocop:disable Lint/UnusedMethodArgument
58
60
  self.const_stack.pop
61
+ self.included_modules.reject! { |depth, _| depth > self.const_stack.length }
62
+ end
63
+
64
+ def include_module(name: nil)
65
+ prefixes = self.const_stack.reduce([]) { |a, c| a << [a.last, c].compact.join('::') }
66
+ full_name = ([name] + prefixes.reverse.map { |prefix| "#{prefix}::#{name}" }).find { |n| Object.const_get(n) rescue nil }
67
+ self.included_modules << [self.const_stack.length, full_name || name]
59
68
  end
60
69
 
61
70
  def const_assigned(const_name: nil, alias_target: nil)
@@ -81,6 +90,7 @@ module RuboCop
81
90
  unless local_only
82
91
  result = Object.const_get(name) rescue nil # Defined elsewhere, top-level
83
92
  result ||= self.defined_constants.find { |c| Object.const_get("#{c}::#{name}") rescue nil } # Defined elsewhere, nested
93
+ result ||= self.included_modules.map(&:last).find { |m| Object.const_get("#{m}::#{name}") rescue nil } # From an included module
84
94
  end
85
95
 
86
96
  result ||= self.defined_constants.find { |c| name == c } # Defined in this file, other module/class
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module RequireTools
3
- VERSION = '0.2.0'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ['lib']
28
28
 
29
- spec.add_runtime_dependency 'rubocop', '>= 0.49.1'
29
+ spec.add_runtime_dependency 'rubocop', '>= 1.0'
30
30
 
31
31
  spec.add_development_dependency 'pry'
32
32
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-require_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manu Wallner
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.49.1
18
+ version: '1.0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.49.1
25
+ version: '1.0'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: pry
28
28
  requirement: !ruby/object:Gem::Requirement