Skip to content

Commit

Permalink
Add Style/EmptyLineAfterMagicComment cop
Browse files Browse the repository at this point in the history
  • Loading branch information
backus committed Jan 24, 2017
1 parent 2e70d6f commit e17fecf
Show file tree
Hide file tree
Showing 25 changed files with 229 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### New features

* [#3893](https://github.com/bbatsov/rubocop/issues/3893): Add a new configuration, `IncludeActiveSupportAliases`, to `Performance/DoublStartEndWith`. This configuration will check for ActiveSupport's `starts_with?` and `ends_with?`. ([@rrosenblum][])
* [#3889](https://github.com/bbatsov/rubocop/pull/3889): Add new `Style/EmptyLineAfterMagicComment` cop. ([@backus][])

### Changes

Expand Down
5 changes: 5 additions & 0 deletions config/enabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,11 @@ Style/NestedTernaryOperator:
StyleGuide: '#no-nested-ternary'
Enabled: true

Style/EmptyLineAfterMagicComment:
Description: 'Add a newline after magic comments to separate them from code.'
StyleGuide: '#separate-magic-comments-from-code'
Enabled: true

Style/Next:
Description: 'Use `next` to skip iteration instead of a condition at the end.'
StyleGuide: '#no-nested-conditionals'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
require 'rubocop/cop/style/else_alignment'
require 'rubocop/cop/style/empty_case_condition'
require 'rubocop/cop/style/empty_else'
require 'rubocop/cop/style/empty_line_after_magic_comment'
require 'rubocop/cop/style/empty_line_between_defs'
require 'rubocop/cop/style/empty_lines'
require 'rubocop/cop/style/empty_lines_around_access_modifier'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/bundler/ordered_gems.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Bundler
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/cop.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require 'uri'

module RuboCop
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/cop/style/documentation_method.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
Expand Down
63 changes: 63 additions & 0 deletions lib/rubocop/cop/style/empty_line_after_magic_comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
# Checks for a newline after the final magic comment.
#
# @example
# # good
# # frozen_string_literal: true
#
# # Some documentation for Person
# class Person
# # Some code
# end
#
# # bad
# # frozen_string_literal: true
# # Some documentation for Person
# class Person
# # Some code
# end
class EmptyLineAfterMagicComment < Cop
MSG = 'Add a space after magic comments.'.freeze
BLANK_LINE = /\A\s*\z/

def investigate(source)
return unless source.ast &&
(last_magic_comment = last_magic_comment(source))
return if source[last_magic_comment.loc.line] =~ BLANK_LINE

offending_range =
source_range(source.buffer, last_magic_comment.loc.line + 1, 0)

add_offense(offending_range, offending_range)
end

def autocorrect(token)
lambda do |corrector|
corrector.insert_before(token, "\n")
end
end

private

# Find the last magic comment in the source file.
#
# Take all comments that precede the first line of code, select the
# magic comments, and return the last magic comment in the file.
#
# @return [Parser::Source::Comment] if magic comments exist before code
# @return [nil] otherwise
def last_magic_comment(source)
source
.comments
.take_while { |comment| comment.loc.line < source.ast.loc.line }
.select { |comment| MagicComment.parse(comment.text).any? }
.last
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/style/stabby_lambda_parentheses.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Style
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/util.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true
# rubocop:disable Metrics/ModuleLength

# rubocop:disable Metrics/ModuleLength
module RuboCop
module Cop
# This module contains a collection of useful utility methods.
Expand Down
14 changes: 13 additions & 1 deletion lib/rubocop/magic_comment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def initialize(comment)
@comment = comment
end

def any?
frozen_string_literal_specified? || encoding_specified?
end

# Does the magic comment enable the frozen string literal feature.
#
# Test whether the frozen string literal value is `true`. Cannot
Expand All @@ -42,7 +46,7 @@ def frozen_string_literal?
#
# @return [Boolean]
def frozen_string_literal_specified?
!frozen_string_literal.nil?
specified?(frozen_string_literal)
end

# Expose the `frozen_string_literal` value coerced to a boolean if possible.
Expand All @@ -61,8 +65,16 @@ def frozen_string_literal
end
end

def encoding_specified?
specified?(encoding)
end

private

def specified?(value)
!value.nil?
end

# Match the entire comment string with a pattern and take the first capture.
#
# @param pattern [Regexp]
Expand Down
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ In the following section you find all available cops:
* [Style/ElseAlignment](cops_style.md#styleelsealignment)
* [Style/EmptyCaseCondition](cops_style.md#styleemptycasecondition)
* [Style/EmptyElse](cops_style.md#styleemptyelse)
* [Style/EmptyLineAfterMagicComment](cops_style.md#styleemptylineaftermagiccomment)
* [Style/EmptyLineBetweenDefs](cops_style.md#styleemptylinebetweendefs)
* [Style/EmptyLines](cops_style.md#styleemptylines)
* [Style/EmptyLinesAroundAccessModifier](cops_style.md#styleemptylinesaroundaccessmodifier)
Expand Down
27 changes: 27 additions & 0 deletions manual/cops_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,33 @@ EnforcedStyle | both
SupportedStyles | empty, nil, both


## Style/EmptyLineAfterMagicComment

Enabled by default | Supports autocorrection
--- | ---
Enabled | Yes

Checks for a newline after the final magic comment.

### Example

```ruby
# good
# frozen_string_literal: true

# Some documentation for Person
class Person
# Some code
end

# bad
# frozen_string_literal: true
# Some documentation for Person
class Person
# Some code
end
```

## Style/EmptyLineBetweenDefs

Enabled by default | Supports autocorrection
Expand Down
5 changes: 5 additions & 0 deletions spec/rubocop/cli/cli_auto_gen_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'y ',
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
"\tx = 0",
'puts x',
'',
Expand Down Expand Up @@ -189,6 +190,7 @@
'y ',
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
'#' * 85,
"\tx = 0",
'puts x'])
Expand Down Expand Up @@ -266,9 +268,11 @@

it 'does not generate configuration for the Syntax cop' do
create_file('example1.rb', ['# encoding: utf-8',
'',
'x = < ', # Syntax error
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
"\tx = 0",
'puts x'])
expect(cli.run(['--auto-gen-config'])).to eq(1)
Expand Down Expand Up @@ -364,6 +368,7 @@
'y ',
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
"\tx = 0",
'puts x',
'',
Expand Down
23 changes: 13 additions & 10 deletions spec/rubocop/cli/cli_options_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ def full_description_of_cop(cop)
'y ',
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
"\tx",
'def a',
' puts',
Expand Down Expand Up @@ -749,18 +750,18 @@ def full_description_of_cop(cop)
' (column 0 instead of 1).',
'# encoding: utf-8',
'^^^^^^^^^^^^^^^^^',
'example2.rb:2:1: C: Tab detected.',
'example2.rb:3:1: C: Tab detected.',
"\tx",
'^',
'example2.rb:2:2: C: Indentation of first line in file ' \
'example2.rb:3:2: C: Indentation of first line in file ' \
'detected.',
"\tx",
' ^',
'example2.rb:3:1: C: Inconsistent indentation ' \
'example2.rb:4:1: C: Inconsistent indentation ' \
'detected.',
'def a ...',
'^^^^^',
'example2.rb:4:1: C: Use 2 (not 3) spaces for ' \
'example2.rb:5:1: C: Use 2 (not 3) spaces for ' \
'indentation.',
' puts',
'^^^',
Expand Down Expand Up @@ -789,25 +790,27 @@ def full_description_of_cop(cop)
context 'when emacs format is specified' do
it 'outputs with emacs format' do
create_file('example1.rb', ['# encoding: utf-8',
'',
'x= 0 ',
'y ',
'puts x'])
create_file('example2.rb', ['# encoding: utf-8',
'',
"\tx = 0",
'puts x'])
expect(cli.run(['--format', 'emacs', 'example1.rb',
'example2.rb'])).to eq(1)
expected_output =
["#{abs('example1.rb')}:2:2: C: Surrounding space missing" \
["#{abs('example1.rb')}:3:2: C: Surrounding space missing" \
' for operator `=`.',
"#{abs('example1.rb')}:2:5: C: Trailing whitespace detected.",
"#{abs('example1.rb')}:3:2: C: Trailing whitespace detected.",
"#{abs('example1.rb')}:3:5: C: Trailing whitespace detected.",
"#{abs('example1.rb')}:4:2: C: Trailing whitespace detected.",
"#{abs('example2.rb')}:1:1: C: Incorrect indentation detected" \
' (column 0 instead of 1).',
"#{abs('example2.rb')}:2:1: C: Tab detected.",
"#{abs('example2.rb')}:2:2: C: Indentation of first line in " \
"#{abs('example2.rb')}:3:1: C: Tab detected.",
"#{abs('example2.rb')}:3:2: C: Indentation of first line in " \
'file detected.',
"#{abs('example2.rb')}:3:1: C: Inconsistent indentation " \
"#{abs('example2.rb')}:4:1: C: Inconsistent indentation " \
'detected.',
''].join("\n")
expect($stdout.string).to eq(expected_output)
Expand Down
5 changes: 4 additions & 1 deletion spec/rubocop/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def interrupt
context 'when super is used with a block' do
it 'still returns 0' do
create_file('example.rb', ['# frozen_string_literal: true',
'',
'# this is a class',
'class Thing',
' def super_with_block',
Expand Down Expand Up @@ -244,13 +245,15 @@ def interrupt
' EnforcedStyle: always'])
expect(cli.run(['--format', 'offenses', '-a', 'example.rb'])).to eq(0)
expect($stdout.string).to eq(['',
'1 Style/EmptyLineAfterMagicComment',
'1 Style/FrozenStringLiteralComment',
'--',
'1 Total',
'2 Total',
'',
''].join("\n"))
expect(IO.read('example.rb'))
.to eq(['# frozen_string_literal: true',
'',
'a = 1 # rubocop:disable Lint/UselessAssignment',
''].join("\n"))
end
Expand Down
1 change: 1 addition & 0 deletions spec/rubocop/cop/rails/http_positional_arguments_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Rails::HttpPositionalArguments do
Expand Down
1 change: 1 addition & 0 deletions spec/rubocop/cop/rails/request_referer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Rails::RequestReferer, :config do
Expand Down
1 change: 1 addition & 0 deletions spec/rubocop/cop/rails/safe_navigation_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Rails::SafeNavigation, :config do
Expand Down
1 change: 1 addition & 0 deletions spec/rubocop/cop/rails/save_bang_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

require 'spec_helper'

describe RuboCop::Cop::Rails::SaveBang do
Expand Down
Loading

0 comments on commit e17fecf

Please sign in to comment.