# frozen_string_literal: truemoduleRuboCopmoduleCopmoduleLint# This cop checks for redundant access modifiers, including those with no# code, those which are repeated, and leading `public` modifiers in a# class or module body. Conditionally-defined methods are considered as# always being defined, and thus access modifiers guarding such methods# are not redundant.## @example## class Foo# public # this is redundant (default access is public)## def method# end## private # this is not redundant (a method is defined)# def method2# end## private # this is redundant (no following methods are defined)# end## @example## class Foo# # The following is not redundant (conditionally defined methods are# # considered as always defining a method)# private## if condition?# def method# end# end## protected # this is not redundant (method is defined)## define_method(:method2) do# end## protected # this is redundant (repeated from previous modifier)## [1,2,3].each do |i|# define_method("foo#{i}") do# end# end## # The following is redundant (methods defined on the class'# # singleton class are not affected by the public modifier)# public## def self.method3# end# end## @example# # Lint/UselessAccessModifier:# # ContextCreatingMethods:# # - concerning# require 'active_support/concern'# class Foo# concerning :Bar do# def some_public_method# end## private## def some_private_method# end# end## # this is not redundant because `concerning` created its own context# private## def some_other_private_method# end# end## @example# # Lint/UselessAccessModifier:# # MethodCreatingMethods:# # - delegate# require 'active_support/core_ext/module/delegation'# class Foo# # this is not redundant because `delegate` creates methods# private## delegate :method_a, to: :method_b# endclassUselessAccessModifier<CopMSG='Useless `%<current>s` access modifier.'defon_class(node)check_node(node.children[2])# class bodyenddefon_module(node)check_node(node.children[1])# module bodyenddefon_block(node)returnunlesseval_call?(node)check_node(node.body)enddefon_sclass(node)check_node(node.children[1])# singleton class bodyendprivatedef_node_matcher:static_method_definition?,<<~PATTERN
{def (send nil? {:attr :attr_reader :attr_writer :attr_accessor} ...)}
PATTERNdef_node_matcher:dynamic_method_definition?,<<~PATTERN
{(send nil? :define_method ...) (block (send nil? :define_method ...) ...)}
PATTERNdef_node_matcher:class_or_instance_eval?,<<~PATTERN
(block (send _ {:class_eval :instance_eval}) ...)
PATTERNdef_node_matcher:class_or_module_or_struct_new_call?,<<~PATTERN
(block (send (const nil? {:Class :Module :Struct}) :new ...) ...)
PATTERNdefcheck_node(node)returnifnode.nil?ifnode.begin_type?check_scope(node)elsifnode.send_type?&&node.bare_access_modifier?add_offense(node,message: format(MSG,current: node.method_name))endenddefaccess_modifier?(node)node.bare_access_modifier?||node.method_name==:private_class_methodenddefcheck_scope(node)cur_vis,unused=check_child_nodes(node,nil,:public)add_offense(unused,message: format(MSG,current: cur_vis))ifunusedenddefcheck_child_nodes(node,unused,cur_vis)node.child_nodes.eachdo|child|ifchild.send_type?&&access_modifier?(child)cur_vis,unused=check_send_node(child,cur_vis,unused)elsifmethod_definition?(child)unused=nilelsifstart_of_new_scope?(child)check_scope(child)elsif!child.defs_type?cur_vis,unused=check_child_nodes(child,unused,cur_vis)endend[cur_vis,unused]enddefcheck_send_node(node,cur_vis,unused)ifnode.bare_access_modifier?check_new_visibility(node,unused,node.method_name,cur_vis)elsifnode.method_name==:private_class_method&&!node.arguments?add_offense(node,message: format(MSG,current: node.method_name))[cur_vis,unused]endenddefcheck_new_visibility(node,unused,new_vis,cur_vis)# does this modifier just repeat the existing visibility?ifnew_vis==cur_visadd_offense(node,message: format(MSG,current: cur_vis))else# was the previous modifier never applied to any defs?ifunusedadd_offense(unused,message: format(MSG,current: cur_vis))end# once we have already warned about a certain modifier, don't# warn again even if it is never applied to any method defsunused=nodeend[new_vis,unused]enddefmethod_definition?(child)static_method_definition?(child)||dynamic_method_definition?(child)||any_method_definition?(child)enddefany_method_definition?(child)cop_config.fetch('MethodCreatingMethods',[]).any?do|m|matcher_name="#{m}_method?".to_symunlessrespond_to?(matcher_name)self.class.def_node_matchermatcher_name,<<~PATTERN
{def (send nil? :#{m} ...)}
PATTERNendsend(matcher_name,child)endenddefstart_of_new_scope?(child)child.module_type?||child.class_type?||child.sclass_type?||eval_call?(child)enddefeval_call?(child)class_or_instance_eval?(child)||class_or_module_or_struct_new_call?(child)||any_context_creating_methods?(child)enddefany_context_creating_methods?(child)cop_config.fetch('ContextCreatingMethods',[]).any?do|m|matcher_name="#{m}_block?".to_symunlessrespond_to?(matcher_name)self.class.def_node_matchermatcher_name,<<~PATTERN
(block (send {nil? const} {:#{m}} ...) ...)
PATTERNendsend(matcher_name,child)endendendendendend