gitlab-styles 6.6.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab/changelog_config.yml +13 -0
  3. data/.gitlab/merge_request_templates/Release.md +9 -31
  4. data/.gitlab-ci.yml +3 -1
  5. data/Dangerfile +5 -0
  6. data/gitlab-styles.gemspec +1 -0
  7. data/lib/gitlab/styles/common/banned_constants.rb +28 -0
  8. data/lib/gitlab/styles/rubocop/model_helpers.rb +1 -1
  9. data/lib/gitlab/styles/rubocop.rb +2 -2
  10. data/lib/gitlab/styles/version.rb +1 -1
  11. data/lib/rubocop/cop/active_record_dependent.rb +32 -0
  12. data/lib/rubocop/cop/active_record_serialize.rb +20 -0
  13. data/lib/rubocop/cop/avoid_return_from_blocks.rb +77 -0
  14. data/lib/rubocop/cop/code_reuse/active_record.rb +80 -0
  15. data/lib/rubocop/cop/custom_error_class.rb +69 -0
  16. data/lib/rubocop/cop/fips/md5.rb +27 -0
  17. data/lib/rubocop/cop/fips/open_ssl.rb +31 -0
  18. data/lib/rubocop/cop/fips/sha1.rb +27 -0
  19. data/lib/rubocop/cop/gem_fetcher.rb +37 -0
  20. data/lib/rubocop/cop/in_batches.rb +18 -0
  21. data/lib/rubocop/cop/internal_affairs/deprecate_cop_helper.rb +39 -0
  22. data/lib/rubocop/cop/line_break_after_guard_clauses.rb +100 -0
  23. data/lib/rubocop/cop/line_break_around_conditional_block.rb +128 -0
  24. data/lib/rubocop/cop/migration/update_large_table.rb +60 -0
  25. data/lib/rubocop/cop/performance/rubyzip.rb +35 -0
  26. data/lib/rubocop/cop/polymorphic_associations.rb +25 -0
  27. data/lib/rubocop/cop/rails/include_url_helper.rb +27 -0
  28. data/lib/rubocop/cop/redirect_with_status.rb +46 -0
  29. data/lib/rubocop/cop/rspec/base.rb +14 -0
  30. data/lib/rubocop/cop/rspec/empty_line_after_final_let_it_be.rb +47 -0
  31. data/lib/rubocop/cop/rspec/empty_line_after_let_block.rb +61 -0
  32. data/lib/rubocop/cop/rspec/empty_line_after_shared_example.rb +61 -0
  33. data/lib/rubocop/cop/rspec/example_starting_character.rb +120 -0
  34. data/lib/rubocop/cop/rspec/have_link_parameters.rb +44 -0
  35. data/lib/rubocop/cop/rspec/single_line_hook.rb +41 -0
  36. data/lib/rubocop/cop/rspec/verbose_include_metadata.rb +71 -0
  37. data/lib/rubocop/cop/style/hash_transformation.rb +83 -0
  38. data/lib/rubocop/cop/style/open_struct_use.rb +39 -0
  39. data/lib/rubocop/cop/without_reactive_cache.rb +16 -0
  40. data/rubocop-default.yml +1 -0
  41. data/rubocop-fips.yml +15 -0
  42. data/rubocop-rspec.yml +3 -2
  43. metadata +49 -27
  44. data/lib/gitlab/styles/rubocop/cop/active_record_dependent.rb +0 -32
  45. data/lib/gitlab/styles/rubocop/cop/active_record_serialize.rb +0 -24
  46. data/lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb +0 -130
  47. data/lib/gitlab/styles/rubocop/cop/custom_error_class.rb +0 -73
  48. data/lib/gitlab/styles/rubocop/cop/gem_fetcher.rb +0 -41
  49. data/lib/gitlab/styles/rubocop/cop/in_batches.rb +0 -22
  50. data/lib/gitlab/styles/rubocop/cop/internal_affairs/deprecate_cop_helper.rb +0 -43
  51. data/lib/gitlab/styles/rubocop/cop/line_break_after_guard_clauses.rb +0 -104
  52. data/lib/gitlab/styles/rubocop/cop/line_break_around_conditional_block.rb +0 -132
  53. data/lib/gitlab/styles/rubocop/cop/migration/update_large_table.rb +0 -64
  54. data/lib/gitlab/styles/rubocop/cop/performance/rubyzip.rb +0 -39
  55. data/lib/gitlab/styles/rubocop/cop/polymorphic_associations.rb +0 -29
  56. data/lib/gitlab/styles/rubocop/cop/rails/include_url_helper.rb +0 -31
  57. data/lib/gitlab/styles/rubocop/cop/redirect_with_status.rb +0 -50
  58. data/lib/gitlab/styles/rubocop/cop/rspec/base.rb +0 -18
  59. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_final_let_it_be.rb +0 -51
  60. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_let_block.rb +0 -65
  61. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_shared_example.rb +0 -65
  62. data/lib/gitlab/styles/rubocop/cop/rspec/example_starting_character.rb +0 -124
  63. data/lib/gitlab/styles/rubocop/cop/rspec/have_link_parameters.rb +0 -48
  64. data/lib/gitlab/styles/rubocop/cop/rspec/single_line_hook.rb +0 -45
  65. data/lib/gitlab/styles/rubocop/cop/rspec/verbose_include_metadata.rb +0 -75
  66. data/lib/gitlab/styles/rubocop/cop/style/hash_transformation.rb +0 -87
  67. data/lib/gitlab/styles/rubocop/cop/style/open_struct_use.rb +0 -43
  68. data/lib/gitlab/styles/rubocop/cop/without_reactive_cache.rb +0 -20
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubocop
4
+ module Cop
5
+ # Ensures a line break after guard clauses.
6
+ #
7
+ # @example
8
+ # # bad
9
+ # return unless condition
10
+ # do_stuff
11
+ #
12
+ # # good
13
+ # return unless condition
14
+ #
15
+ # do_stuff
16
+ #
17
+ # # bad
18
+ # raise if condition
19
+ # do_stuff
20
+ #
21
+ # # good
22
+ # raise if condition
23
+ #
24
+ # do_stuff
25
+ #
26
+ # Multiple guard clauses are allowed without
27
+ # line break.
28
+ #
29
+ # # good
30
+ # return unless condition_a
31
+ # return unless condition_b
32
+ #
33
+ # do_stuff
34
+ #
35
+ # Guard clauses in case statement are allowed without
36
+ # line break.
37
+ #
38
+ # # good
39
+ # case model
40
+ # when condition_a
41
+ # return true unless condition_b
42
+ # when
43
+ # ...
44
+ # end
45
+ #
46
+ # Guard clauses before end are allowed without
47
+ # line break.
48
+ #
49
+ # # good
50
+ # if condition_a
51
+ # do_something
52
+ # else
53
+ # do_something_else
54
+ # return unless condition
55
+ # end
56
+ #
57
+ # do_something_more
58
+ class LineBreakAfterGuardClauses < RuboCop::Cop::Cop
59
+ MSG = 'Add a line break after guard clauses'
60
+
61
+ def_node_matcher :guard_clause_node?, <<-PATTERN
62
+ [{(send nil? {:raise :fail :throw} ...) return break next} single_line?]
63
+ PATTERN
64
+
65
+ def on_if(node)
66
+ return unless node.single_line?
67
+ return unless guard_clause?(node)
68
+ return if next_line(node).blank? || clause_last_line?(next_line(node)) || guard_clause?(next_sibling(node))
69
+
70
+ add_offense(node)
71
+ end
72
+
73
+ def autocorrect(node)
74
+ lambda do |corrector|
75
+ corrector.insert_after(node.loc.expression, "\n")
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def guard_clause?(node)
82
+ return false unless node.if_type?
83
+
84
+ guard_clause_node?(node.if_branch)
85
+ end
86
+
87
+ def next_sibling(node)
88
+ node.parent.children[node.sibling_index + 1]
89
+ end
90
+
91
+ def next_line(node)
92
+ processed_source[node.loc.line]
93
+ end
94
+
95
+ def clause_last_line?(line)
96
+ line =~ /^\s*(?:end|elsif|else|when|rescue|ensure)/
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubocop
4
+ module Cop
5
+ # Ensures a line break around conditional blocks.
6
+ #
7
+ # @example
8
+ # # bad
9
+ # do_something
10
+ # if condition
11
+ # do_extra_stuff
12
+ # end
13
+ # do_something_more
14
+ #
15
+ # # good
16
+ # do_something
17
+ #
18
+ # if condition
19
+ # do_extra_stuff
20
+ # end
21
+ #
22
+ # do_something_more
23
+ #
24
+ # # bad
25
+ # do_something
26
+ # unless condition
27
+ # do_extra_stuff
28
+ # end
29
+ #
30
+ # do_something_more
31
+ #
32
+ # # good
33
+ # def a_method
34
+ # if condition
35
+ # do_something
36
+ # end
37
+ # end
38
+ #
39
+ # # good
40
+ # on_block do
41
+ # if condition
42
+ # do_something
43
+ # end
44
+ # end
45
+ class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
46
+ include RuboCop::Cop::RangeHelp
47
+
48
+ MSG = 'Add a line break around conditional blocks'
49
+
50
+ def on_if(node)
51
+ # This cop causes errors in haml files, so let's skip those
52
+ return if in_haml?(node)
53
+ return if node.single_line?
54
+ return unless node.if? || node.unless?
55
+
56
+ add_offense(node) unless previous_line_valid?(node)
57
+ add_offense(node) unless last_line_valid?(node)
58
+ end
59
+
60
+ def autocorrect(node)
61
+ lambda do |corrector|
62
+ line = range_by_whole_lines(node.source_range)
63
+
64
+ corrector.insert_before(line, "\n") unless previous_line_valid?(node)
65
+ corrector.insert_after(line, "\n") unless last_line_valid?(node)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def previous_line_valid?(node)
72
+ previous_line(node).empty? ||
73
+ start_clause_line?(previous_line(node)) ||
74
+ block_start?(previous_line(node)) ||
75
+ begin_line?(previous_line(node)) ||
76
+ assignment_line?(previous_line(node)) ||
77
+ rescue_line?(previous_line(node))
78
+ end
79
+
80
+ def last_line_valid?(node)
81
+ last_line(node).empty? ||
82
+ end_line?(last_line(node)) ||
83
+ end_clause_line?(last_line(node))
84
+ end
85
+
86
+ def previous_line(node)
87
+ processed_source[node.loc.line - 2]
88
+ end
89
+
90
+ def last_line(node)
91
+ processed_source[node.loc.last_line]
92
+ end
93
+
94
+ def start_clause_line?(line)
95
+ line =~ /^\s*(def|=end|#|module|class|if|unless|else|elsif|ensure|when)/
96
+ end
97
+
98
+ def end_clause_line?(line)
99
+ line =~ /^\s*(#|rescue|else|elsif|when)/
100
+ end
101
+
102
+ def begin_line?(line)
103
+ # an assignment followed by a begin or ust a begin
104
+ line =~ /^\s*(@?(\w|\|+|=|\[|\]|\s)+begin|begin)/
105
+ end
106
+
107
+ def assignment_line?(line)
108
+ line =~ /^\s*.*=/
109
+ end
110
+
111
+ def rescue_line?(line)
112
+ line =~ /^\s*rescue/
113
+ end
114
+
115
+ def block_start?(line)
116
+ line.match(/ (do|{)( \|.*?\|)?\s?(#.+)?\z/)
117
+ end
118
+
119
+ def end_line?(line)
120
+ line =~ /^\s*(end|})/
121
+ end
122
+
123
+ def in_haml?(node)
124
+ node.location.expression.source_buffer.name.end_with?('.haml.rb')
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../../../gitlab/styles/rubocop/migration_helpers'
3
+
4
+ module Rubocop
5
+ module Cop
6
+ module Migration
7
+ # This cop checks for methods that may lead to batch type issues on a table that's been
8
+ # explicitly denied because of its size.
9
+ #
10
+ # Even though though these methods perform functions to avoid
11
+ # downtime, using it with tables with millions of rows still causes a
12
+ # significant delay in the deploy process and is best avoided.
13
+ #
14
+ # See https://gitlab.com/gitlab-com/infrastructure/issues/1602 for more
15
+ # information.
16
+ class UpdateLargeTable < RuboCop::Cop::Cop
17
+ include Gitlab::Styles::Rubocop::MigrationHelpers
18
+
19
+ MSG = 'Using `%s` on the `%s` table will take a long time to ' \
20
+ 'complete, and should be avoided unless absolutely ' \
21
+ 'necessary'
22
+
23
+ def_node_matcher :batch_update?, <<~PATTERN
24
+ (send nil? ${#denied_method?}
25
+ (sym $...)
26
+ ...)
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ return if denied_tables.empty? || denied_methods.empty?
31
+ return unless in_migration?(node)
32
+
33
+ matches = batch_update?(node)
34
+ return unless matches
35
+
36
+ update_method = matches.first
37
+ table = matches.last.to_a.first
38
+
39
+ return unless denied_tables.include?(table)
40
+
41
+ add_offense(node, message: format(MSG, update_method, table))
42
+ end
43
+
44
+ private
45
+
46
+ def denied_tables
47
+ cop_config['DeniedTables'] || []
48
+ end
49
+
50
+ def denied_method?(method_name)
51
+ denied_methods.include?(method_name)
52
+ end
53
+
54
+ def denied_methods
55
+ cop_config['DeniedMethods'] || []
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Performance
6
+ # This cop flags inefficient uses of rubyzip's Zip::File, since when instantiated
7
+ # it reads the file's Central Directory into memory entirely. For zips with many
8
+ # files and directories, this can be very expensive even when the archive's size
9
+ # in bytes is small.
10
+ #
11
+ # See also:
12
+ # - https://github.com/rubyzip/rubyzip/issues/506
13
+ # - https://github.com/rubyzip/rubyzip#notes-on-zipinputstream
14
+ class Rubyzip < RuboCop::Cop::Cop
15
+ MSG = 'Be careful when opening or iterating zip files via Zip::File. ' \
16
+ 'Zip archives may contain many entries, and their file index is ' \
17
+ 'read into memory upon construction, which can lead to ' \
18
+ 'high memory use and poor performance. ' \
19
+ 'Consider iterating archive entries via Zip::InputStream instead.'
20
+
21
+ def_node_matcher :reads_central_directory?, <<-PATTERN
22
+ (send
23
+ (const
24
+ (const {nil? (cbase)} :Zip) :File) {:new :open :foreach} ...)
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ return unless reads_central_directory?(node)
29
+
30
+ add_offense(node)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../gitlab/styles/rubocop/model_helpers'
4
+
5
+ module Rubocop
6
+ module Cop
7
+ # Cop that prevents the use of polymorphic associations
8
+ class PolymorphicAssociations < RuboCop::Cop::Cop
9
+ include Gitlab::Styles::Rubocop::ModelHelpers
10
+
11
+ MSG = 'Do not use polymorphic associations, use separate tables instead'
12
+
13
+ def on_send(node)
14
+ return unless in_model?(node)
15
+ return unless node.children[1] == :belongs_to
16
+
17
+ node.children.last.each_node(:pair) do |pair|
18
+ key_name = pair.children[0].children[0]
19
+
20
+ add_offense(pair) if key_name == :polymorphic
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../gitlab/styles/rubocop/model_helpers'
4
+
5
+ module Rubocop
6
+ module Cop
7
+ module Rails
8
+ class IncludeUrlHelper < RuboCop::Cop::Cop
9
+ MSG = <<~MSG
10
+ Avoid including `ActionView::Helpers::UrlHelper`.
11
+ It adds/overrides ~40 methods while usually only one is needed.
12
+ Instead, use the `Gitlab::Routing.url_helpers`/`Application.routes.url_helpers`(outside of gitlab)
13
+ and `ActionController::Base.helpers.link_to`.
14
+ See https://gitlab.com/gitlab-org/gitlab/-/issues/340567.
15
+ MSG
16
+
17
+ def_node_matcher :include_url_helpers_node?, <<~PATTERN
18
+ (send nil? :include (const (const (const {nil? cbase} :ActionView) :Helpers) :UrlHelper))
19
+ PATTERN
20
+
21
+ def on_send(node)
22
+ add_offense(node) if include_url_helpers_node?(node)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubocop
4
+ module Cop
5
+ # This cop prevents usage of 'redirect_to' in actions 'destroy' without specifying 'status'.
6
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/31840
7
+ class RedirectWithStatus < RuboCop::Cop::Cop
8
+ MSG = 'Do not use "redirect_to" without "status" in "destroy" action'
9
+
10
+ def on_def(node)
11
+ return unless in_controller?(node)
12
+ return unless destroy?(node) || destroy_all?(node)
13
+
14
+ node.each_descendant(:send) do |def_node|
15
+ next unless redirect_to?(def_node)
16
+
17
+ methods = []
18
+
19
+ def_node.children.last.each_node(:pair) do |pair|
20
+ methods << pair.children.first.children.first
21
+ end
22
+
23
+ add_offense(def_node, location: :selector) unless methods.include?(:status)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def in_controller?(node)
30
+ node.location.expression.source_buffer.name.end_with?('_controller.rb')
31
+ end
32
+
33
+ def destroy?(node)
34
+ node.children.first == :destroy
35
+ end
36
+
37
+ def destroy_all?(node)
38
+ node.children.first == :destroy_all
39
+ end
40
+
41
+ def redirect_to?(node)
42
+ node.children[1] == :redirect_to
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative '../../../gitlab/styles/rubocop/rspec/helpers'
5
+
6
+ module Rubocop
7
+ module Cop
8
+ module RSpec
9
+ class Base < RuboCop::Cop::RSpec::Base
10
+ include Gitlab::Styles::Rubocop::Rspec::Helpers
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative 'base'
5
+
6
+ module Rubocop
7
+ module Cop
8
+ module RSpec
9
+ # Checks if there is an empty line after the last `let_it_be` block.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # let_it_be(:foo) { bar }
14
+ # let_it_be(:something) { other }
15
+ # it { does_something }
16
+ #
17
+ # # good
18
+ # let_it_be(:foo) { bar }
19
+ # let_it_be(:something) { other }
20
+ #
21
+ # it { does_something }
22
+ class EmptyLineAfterFinalLetItBe < Base
23
+ extend RuboCop::Cop::AutoCorrector
24
+ include RuboCop::RSpec::EmptyLineSeparation
25
+
26
+ MSG = 'Add an empty line after the last `let_it_be`.'
27
+
28
+ def_node_matcher :let_it_be?, <<-PATTERN
29
+ {
30
+ (block (send #rspec? {:let_it_be :let_it_be_with_refind :let_it_be_with_reload} ...) ...)
31
+ (send #rspec? {:let_it_be :let_it_be_with_refind :let_it_be_with_reload} _ block_pass)
32
+ }
33
+ PATTERN
34
+
35
+ def on_block(node)
36
+ return unless example_group_with_body?(node)
37
+
38
+ final_let_it_be = node.body.child_nodes.reverse.find { |child| let_it_be?(child) }
39
+
40
+ return if final_let_it_be.nil?
41
+
42
+ missing_separating_line_offense(final_let_it_be) { MSG }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative 'base'
5
+
6
+ module Rubocop
7
+ module Cop
8
+ module RSpec
9
+ # Checks if there is an empty line after let blocks.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # RSpec.describe Foo do
14
+ # let(:something) { 'something' }
15
+ # let(:another_thing) do
16
+ # end
17
+ # let(:something_else) do
18
+ # end
19
+ # let(:last_thing) { 'last thing' }
20
+ # end
21
+ #
22
+ # # good
23
+ # RSpec.describe Foo do
24
+ # let(:something) { 'something' }
25
+ # let(:another_thing) do
26
+ # end
27
+ #
28
+ # let(:something_else) do
29
+ # end
30
+ #
31
+ # let(:last_thing) { 'last thing' }
32
+ # end
33
+ #
34
+ # # good - it's ok to have non-separated without do/end blocks
35
+ # RSpec.describe Foo do
36
+ # let(:something) { 'something' }
37
+ # let(:last_thing) { 'last thing' }
38
+ # end
39
+ #
40
+ class EmptyLineAfterLetBlock < Base
41
+ extend RuboCop::Cop::AutoCorrector
42
+ include RuboCop::RSpec::EmptyLineSeparation
43
+
44
+ MSG = 'Add an empty line after `%<let>s` block.'
45
+
46
+ def_node_matcher :lets, LET.block_pattern
47
+
48
+ def on_block(node)
49
+ lets(node) do
50
+ break if last_child?(node)
51
+ next if node.single_line?
52
+
53
+ missing_separating_line_offense(node) do |method|
54
+ format(MSG, let: method)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative 'base'
5
+
6
+ module Rubocop
7
+ module Cop
8
+ module RSpec
9
+ # Checks if there is an empty line after shared example blocks.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # RSpec.describe Foo do
14
+ # it_behaves_like 'do this first'
15
+ # it_behaves_like 'does this' do
16
+ # end
17
+ # it_behaves_like 'does that' do
18
+ # end
19
+ # it_behaves_like 'do some more'
20
+ # end
21
+ #
22
+ # # good
23
+ # RSpec.describe Foo do
24
+ # it_behaves_like 'do this first'
25
+ # it_behaves_like 'does this' do
26
+ # end
27
+ #
28
+ # it_behaves_like 'does that' do
29
+ # end
30
+ #
31
+ # it_behaves_like 'do some more'
32
+ # end
33
+ #
34
+ # # fair - it's ok to have non-separated without blocks
35
+ # RSpec.describe Foo do
36
+ # it_behaves_like 'do this first'
37
+ # it_behaves_like 'does this'
38
+ # end
39
+ #
40
+ class EmptyLineAfterSharedExample < Base
41
+ extend RuboCop::Cop::AutoCorrector
42
+ include RuboCop::RSpec::EmptyLineSeparation
43
+
44
+ MSG = 'Add an empty line after `%<example>s` block.'
45
+
46
+ def_node_matcher :shared_examples,
47
+ (SharedGroups::ALL + Includes::ALL).block_pattern
48
+
49
+ def on_block(node)
50
+ shared_examples(node) do
51
+ break if last_child?(node)
52
+
53
+ missing_separating_line_offense(node) do |method|
54
+ format(MSG, example: method)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end