1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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 105 106 107 108
|
# frozen_string_literal: true
require "rubocop/packaging/lib_helper_module"
module RuboCop # :nodoc:
module Cop # :nodoc:
module Packaging # :nodoc:
# This cop flags the `require` calls, from anywhere mapping to
# the "lib" directory, except originating from lib/.
#
# @example
#
# # bad
# require "../lib/foo/bar"
#
# # good
# require "foo/bar"
#
# # bad
# require File.expand_path("../../lib/foo", __FILE__)
#
# # good
# require "foo"
#
# # bad
# require File.expand_path("../../../lib/foo/bar/baz/qux", __dir__)
#
# # good
# require "foo/bar/baz/qux"
#
# # bad
# require File.dirname(__FILE__) + "/../../lib/baz/qux"
#
# # good
# require "baz/qux"
#
class RequireHardcodingLib < Base
include RuboCop::Packaging::LibHelperModule
extend AutoCorrector
# This is the message that will be displayed when RuboCop::Packaging
# finds an offense of using `require` with relative path to lib.
MSG = "Avoid using `require` with relative path to `lib/`. " \
"Use `require` with absolute path instead."
def_node_matcher :require?, <<~PATTERN
{(send nil? :require (str #falls_in_lib?))
(send nil? :require (send (const nil? :File) :expand_path (str #falls_in_lib?) (send nil? :__dir__)))
(send nil? :require (send (const nil? :File) :expand_path (str #falls_in_lib_using_file?) (str _)))
(send nil? :require (send (send (const nil? :File) :dirname {(str _) (send nil? _)}) :+ (str #falls_in_lib_with_file_dirname_plus_str?)))
(send nil? :require (dstr (begin (send (const nil? :File) :dirname {(str _) (send nil? _)})) (str #falls_in_lib_with_file_dirname_plus_str?)))}
PATTERN
# Extended from the Base class.
# More about the `#on_new_investigation` method can be found here:
# https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb
#
# Processing of the AST happens here.
def on_new_investigation
@file_path = processed_source.file_path
@file_directory = File.dirname(@file_path)
end
# Extended from AST::Traversal.
# More about the `#on_send` method can be found here:
# https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112
def on_send(node)
return unless require?(node)
add_offense(node) do |corrector|
corrector.replace(node, good_require_call)
end
end
# Called from on_send, this method helps to replace
# the "bad" require call with the "good" one.
def good_require_call
good_call = @str.sub(%r{^.*/lib/}, "")
%(require "#{good_call}")
end
# This method is called from inside `#def_node_matcher`.
# It flags an offense if the `require` call is made from
# anywhere except the "lib" directory.
def falls_in_lib?(str)
@str = str
target_falls_in_lib?(str) && inspected_file_is_not_in_lib_or_gemspec?
end
# This method is called from inside `#def_node_matcher`.
# It flags an offense if the `require` call (using the __FILE__
# arguement) is made from anywhere except the "lib" directory.
def falls_in_lib_using_file?(str)
@str = str
target_falls_in_lib_using_file?(str) && inspected_file_is_not_in_lib_or_gemspec?
end
# This method preprends a "." to the string that starts with "/".
# And then determines if that call is made to "lib/".
def falls_in_lib_with_file_dirname_plus_str?(str)
@str = str
str.prepend(".")
target_falls_in_lib?(str) && inspected_file_is_not_in_lib_or_gemspec?
end
end
end
end
end
|