2021-03-29 14:09:37 -04:00
# frozen_string_literal: true
module RuboCop
module Cop
module Gitlab
# Cop that checks for correct calling of #feature_available?
2022-09-14 11:12:56 -04:00
class FeatureAvailableUsage < RuboCop :: Cop :: Base
2021-03-29 14:09:37 -04:00
OBSERVED_METHOD = :feature_available?
LICENSED_FEATURE_LITERAL_ARG_MSG = '`feature_available?` should not be called for features that can be licensed (`%s` given), use `licensed_feature_available?(feature)` instead.'
LICENSED_FEATURE_DYNAMIC_ARG_MSG = " `feature_available?` should not be called for features that can be licensed (`%s` isn't a literal so we cannot say if it's legit or not), using `licensed_feature_available?(feature)` may be more appropriate. "
NOT_ENOUGH_ARGS_MSG = '`feature_available?` should be called with two arguments: `feature` and `user`.'
FEATURES = % i [
issues
forking
merge_requests
wiki
snippets
builds
repository
pages
metrics_dashboard
analytics
operations
2022-09-14 20:14:10 -04:00
monitor
2021-03-29 14:09:37 -04:00
security_and_compliance
container_registry
2022-08-02 05:11:05 -04:00
environments
2022-08-11 14:11:57 -04:00
feature_flags
2022-08-15 02:12:42 -04:00
releases
2021-03-29 14:09:37 -04:00
] . freeze
EE_FEATURES = % i [ requirements ] . freeze
ALL_FEATURES = ( FEATURES + EE_FEATURES ) . freeze
SPECIAL_CLASS = %w[ License ] . freeze
def on_send ( node )
return unless method_name ( node ) == OBSERVED_METHOD
return if caller_is_special_case? ( node )
return if feature_name ( node ) . nil?
return if ALL_FEATURES . include? ( feature_name ( node ) ) && args_count ( node ) == 2
if ! ALL_FEATURES . include? ( feature_name ( node ) )
2022-09-14 11:12:56 -04:00
add_offense ( node , message : licensed_feature_message ( node ) )
2021-03-29 14:09:37 -04:00
elsif args_count ( node ) < 2
2022-09-14 11:12:56 -04:00
add_offense ( node , message : NOT_ENOUGH_ARGS_MSG )
2021-03-29 14:09:37 -04:00
end
end
def licensed_feature_message ( node )
message_template = dynamic_feature? ( node ) ? LICENSED_FEATURE_DYNAMIC_ARG_MSG : LICENSED_FEATURE_LITERAL_ARG_MSG
message_template % feature_arg_name ( node )
end
private
def method_name ( node )
node . children [ 1 ]
end
def class_caller ( node )
node . children [ 0 ] & . const_name . to_s
end
def caller_is_special_case? ( node )
SPECIAL_CLASS . include? ( class_caller ( node ) )
end
def feature_arg ( node )
node . children [ 2 ]
end
def dynamic_feature? ( node )
feature = feature_arg ( node )
return false unless feature
! feature . literal?
end
def feature_name ( node )
feature = feature_arg ( node )
return unless feature
return feature . children . compact . join ( '.' ) if dynamic_feature? ( node )
return feature . value if feature . respond_to? ( :value )
feature . type
end
def feature_arg_name ( node )
feature = feature_arg ( node )
return unless feature
return feature . children . compact . join ( '.' ) if dynamic_feature? ( node )
return feature . children [ 0 ] . inspect if feature . literal?
feature . type
end
def args_count ( node )
node . children [ 2 .. ] . size
end
end
end
end
end