lib/rubocop/cop/rails/root_pathname_methods.rb in rubocop-rails-2.16.0 vs lib/rubocop/cop/rails/root_pathname_methods.rb in rubocop-rails-2.16.1

- old
+ new

@@ -9,10 +9,14 @@ # so we can apply many IO methods directly. # # This cop works best when used together with # `Style/FileRead`, `Style/FileWrite` and `Rails/RootJoinChain`. # + # @safety + # This cop is unsafe for autocorrection because `Dir`'s `children`, `each_child`, `entries`, and `glob` + # methods return string element, but these methods of `Pathname` return `Pathname` element. + # # @example # # bad # File.open(Rails.root.join('db', 'schema.rb')) # File.open(Rails.root.join('db', 'schema.rb'), 'w') # File.read(Rails.root.join('db', 'schema.rb')) @@ -28,10 +32,11 @@ # Rails.root.join('db', 'schema.rb').write(content) # Rails.root.join('db', 'schema.rb').binwrite(content) # class RootPathnameMethods < Base extend AutoCorrector + include RangeHelp MSG = '`%<rails_root>s` is a `Pathname` so you can just append `#%<method>s`.' DIR_METHODS = %i[children delete each_child empty? entries exist? glob mkdir open rmdir unlink].to_set.freeze @@ -136,10 +141,15 @@ (send (const {nil? cbase} :FileTest) $FILE_TEST_METHODS $_ $...) (send (const {nil? cbase} :FileUtils) $FILE_UTILS_METHODS $_ $...) } PATTERN + def_node_matcher :dir_glob?, <<~PATTERN + (send + (const {cbase nil?} :Dir) :glob ...) + PATTERN + def_node_matcher :rails_root_pathname?, <<~PATTERN { $#rails_root? (send $#rails_root? :join ...) } @@ -151,12 +161,16 @@ PATTERN def on_send(node) evidence(node) do |method, path, args, rails_root| add_offense(node, message: format(MSG, method: method, rails_root: rails_root.source)) do |corrector| - replacement = "#{path.source}.#{method}" - replacement += "(#{args.map(&:source).join(', ')})" unless args.empty? + if dir_glob?(node) + replacement = build_path_glob(path, method) + else + replacement = "#{path.source}.#{method}" + replacement += "(#{args.map(&:source).join(', ')})" unless args.empty? + end corrector.replace(node, replacement) end end end @@ -166,9 +180,34 @@ def evidence(node) return if node.method?(:open) && node.parent&.send_type? return unless (method, path, args = pathname_method(node)) && (rails_root = rails_root_pathname?(path)) yield(method, path, args, rails_root) + end + + def build_path_glob(path, method) + receiver = range_between(path.loc.expression.begin_pos, path.children.first.loc.selector.end_pos).source + + argument = if path.arguments.one? + path.first_argument.source + else + join_arguments(path.arguments) + end + + "#{receiver}.#{method}(#{argument})" + end + + def include_interpolation?(arguments) + arguments.any? do |argument| + argument.children.any? { |child| child.respond_to?(:begin_type?) && child.begin_type? } + end + end + + def join_arguments(arguments) + quote = include_interpolation?(arguments) ? '"' : "'" + joined_arguments = arguments.map(&:value).join('/') + + "#{quote}#{joined_arguments}#{quote}" end end end end end