diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-16 12:07:51 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-16 12:07:51 +0000 |
commit | 1c9afffa29584c92bf1bb73c005f69c6875808e6 (patch) | |
tree | 39332df6f417a28296703e28bd7f5c9ab08275d6 /rubocop | |
parent | f15ffb0170776275ff875ddc32bcd79d6c6163eb (diff) | |
download | gitlab-ce-1c9afffa29584c92bf1bb73c005f69c6875808e6.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'rubocop')
-rw-r--r-- | rubocop/cop/gitlab/namespaced_class.rb | 59 |
1 files changed, 51 insertions, 8 deletions
diff --git a/rubocop/cop/gitlab/namespaced_class.rb b/rubocop/cop/gitlab/namespaced_class.rb index 1f1fd280922..914cc8720b9 100644 --- a/rubocop/cop/gitlab/namespaced_class.rb +++ b/rubocop/cop/gitlab/namespaced_class.rb @@ -5,33 +5,76 @@ module RuboCop module Gitlab # Cop that enforces use of namespaced classes in order to better identify # high level domains within the codebase. - + # # @example # # bad # class MyClass # end # + # module Gitlab + # class MyClass + # end + # end + # + # class Gitlab::MyClass + # end + # # # good # module MyDomain # class MyClass # end # end - + # + # module Gitlab + # module MyDomain + # class MyClass + # end + # end + # end + # + # class Gitlab::MyDomain::MyClass + # end class NamespacedClass < RuboCop::Cop::Cop MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/212156' - def_node_matcher :compact_namespaced_class?, <<~PATTERN - (class (const (const ...) ...) ...) - PATTERN + # These namespaces are considered top-level semantically. + # Note: Nested namespace like Foo::Bar are also supported. + PSEUDO_TOPLEVEL = %w[Gitlab] + .map { _1.split('::') }.freeze def on_module(node) - @namespaced = true + add_potential_domain_namespace(node) end def on_class(node) - return if @namespaced + # Add potential namespaces from compact definitions like `class Foo::Bar`. + # Remove class name because it's not a domain namespace. + add_potential_domain_namespace(node) { _1.pop } + + add_offense(node) if domain_namespaces.none? + end + + private + + def domain_namespaces + @domain_namespaces ||= [] + end + + def add_potential_domain_namespace(node) + return if domain_namespaces.any? + + identifiers = identifiers_for(node) + yield(identifiers) if block_given? + + PSEUDO_TOPLEVEL.each do |namespaces| + identifiers.shift(namespaces.size) if namespaces == identifiers.first(namespaces.size) + end + + domain_namespaces.concat(identifiers) + end - add_offense(node) unless compact_namespaced_class?(node) + def identifiers_for(node) + node.identifier.source.sub(/^::/, '').split('::') end end end |