Skip to content

Commit

Permalink
Merge pull request #637 from jonas054/check_indentation_consistency
Browse files Browse the repository at this point in the history
[Fix #631] Check that consecutive lines have the same indentation.
  • Loading branch information
bbatsov committed Nov 19, 2013
2 parents d2f822a + b97819f commit f860b48
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* New Rails cop `DefaultScope` ensures `default_scope` is called properly with a block argument. ([@bbatsov][])
* All cops now support the `Include` param, which specifies the files on which they should operate. ([@bbatsov][])
* All cops now support the `Exclude` param, which specifies the files on which they should not operate. ([@bbatsov][])
* [#631](https://github.com/bbatsov/rubocop/issues/631): `IndentationWidth` cop now detects inconsistent indentation between lines that should have the same indentation. ([@jonas054][])

### Bugs fixed

Expand Down
30 changes: 26 additions & 4 deletions lib/rubocop/cop/style/indentation_width.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ class IndentationWidth < Cop
include CheckMethods

CORRECT_INDENTATION = 2
MSG = "Use #{CORRECT_INDENTATION} (not %d) spaces for indentation."

def on_begin(node)
check_consistent(node)
end

def on_kwbegin(node)
node.children.each { |c| check_indentation(node.loc.end, c) }
check_indentation(node.loc.end, node.children.first)
check_consistent(node)
end

def on_block(node)
Expand Down Expand Up @@ -50,7 +54,9 @@ def on_for(node)

def on_while(node)
_condition, body = *node
check_indentation(node.loc.keyword, body)
if node.loc.keyword.begin_pos == node.loc.expression.begin_pos
check_indentation(node.loc.keyword, body)
end
end

alias_method :on_until, :on_while
Expand Down Expand Up @@ -135,7 +141,23 @@ def check_indentation(base_loc, body_node)
convention(nil,
Parser::Source::Range.new(expr.source_buffer,
begin_pos, end_pos),
sprintf(MSG, indentation))
sprintf("Use #{CORRECT_INDENTATION} (not %d) spaces " +
'for indentation.', indentation))
end
end

def check_consistent(node)
node.children.map(&:loc).each_cons(2) do |child1, child2|
if child2.line > child1.line && child2.column != child1.column
expr = child2.expression
indentation = expr.source_line =~ /\S/
end_pos = expr.begin_pos
begin_pos = end_pos - indentation
convention(nil,
Parser::Source::Range.new(expr.source_buffer,
begin_pos, end_pos),
'Inconsistent indentation detected.')
end
end
end
end
Expand Down
11 changes: 10 additions & 1 deletion spec/rubocop/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ def abs(path)
'# one by one as the offences are removed from the code ' +
'base.',
'',
'IndentationWidth:',
' Enabled: false',
'',
'LineLength:',
' Enabled: false',
'',
Expand Down Expand Up @@ -323,6 +326,10 @@ def full_description_of_cop(cop)
'example2.rb:2:1: C: Tab detected.',
"\tx",
'^',
'example2.rb:3:1: C: Inconsistent indentation ' +
'detected.',
'def a',
'',
'example2.rb:4:1: C: Use 2 (not 3) spaces for ' +
'indentation.',
' puts',
Expand All @@ -340,7 +347,7 @@ def full_description_of_cop(cop)
' end',
' ^^^',
'',
'3 files inspected, 9 offences detected',
'3 files inspected, 10 offences detected',
''].join("\n"))
end
end
Expand All @@ -362,6 +369,8 @@ def full_description_of_cop(cop)
"#{abs('example1.rb')}:2:5: C: Trailing whitespace detected.",
"#{abs('example1.rb')}:3:2: C: Trailing whitespace detected.",
"#{abs('example2.rb')}:2:1: C: Tab detected.",
"#{abs('example2.rb')}:3:1: C: Inconsistent indentation " +
'detected.',
''].join("\n")
expect($stdout.string).to eq(expected_output)
end
Expand Down
15 changes: 6 additions & 9 deletions spec/rubocop/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@
end

it 'raises validation error' do
e = described_class::ValidationError
expect { configuration.validate }.to raise_error(e) do |error|
expect(error.message).to start_with('unrecognized cop LyneLenth')
end
expect { configuration.validate }
.to raise_error(described_class::ValidationError,
/^unrecognized cop LyneLenth/)
end
end

Expand All @@ -49,11 +48,9 @@
end

it 'raises validation error' do
e = described_class::ValidationError
expect { configuration.validate }.to raise_error(e) do |error|
expect(error.message).to
start_with('unrecognized parameter LineLength:Min')
end
expect { configuration.validate }
.to raise_error(described_class::ValidationError,
/^unrecognized parameter LineLength:Min/)
end
end
end
Expand Down
73 changes: 53 additions & 20 deletions spec/rubocop/cop/style/indentation_width_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
inspect_source(cop,
['if cond',
' func',
' func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of an else body' do
Expand All @@ -21,9 +23,11 @@
' func1',
'else',
' func2',
' func2',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of an elsif body' do
Expand All @@ -32,11 +36,13 @@
' b1',
'elsif a2',
' b2',
'b3',
'else',
' c',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers offence for bad indentation of ternary inside else' do
Expand Down Expand Up @@ -124,9 +130,11 @@
inspect_source(cop,
['unless cond',
' func',
' func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty unless' do
Expand All @@ -144,9 +152,11 @@
['case a',
'when b',
' c',
' d',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation in a case/else body' do
Expand All @@ -158,16 +168,19 @@
' e',
'else',
' f',
' g',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 3) spaces for indentation.'])
.to eq(['Use 2 (not 3) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts correctly indented case/when/else' do
inspect_source(cop,
['case a',
'when b',
' c',
' c',
'when d',
'else',
' f',
Expand Down Expand Up @@ -216,29 +229,33 @@
inspect_source(cop,
['while cond',
' func',
' func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of begin/end/while' do
inspect_source(cop,
['begin',
['something = begin',
' func1',
' func2',
'end while cond'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.',
'Use 2 (not 3) spaces for indentation.'])
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of an until body' do
inspect_source(cop,
['until cond',
' func',
' func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty while' do
Expand All @@ -254,9 +271,11 @@
inspect_source(cop,
['for var in 1..10',
' func',
'func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty for' do
Expand All @@ -272,19 +291,22 @@
inspect_source(cop,
['def test',
' func1',
' func2', # No offence registered for this.
' func2',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 4) spaces for indentation.'])
.to eq(['Use 2 (not 4) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of a defs body' do
inspect_source(cop,
['def self.test',
' func',
' func',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 3) spaces for indentation.'])
.to eq(['Use 2 (not 3) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty def body' do
Expand All @@ -306,11 +328,14 @@
it 'registers an offence for bad indentation of a class body' do
inspect_source(cop,
['class Test',
' def func',
' def func1',
' end',
' def func2',
' end',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 4) spaces for indentation.'])
.to eq(['Use 2 (not 4) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty class body' do
Expand All @@ -325,11 +350,14 @@
it 'registers an offence for bad indentation of a module body' do
inspect_source(cop,
['module Test',
' def func',
' def func1',
' end',
' def func2',
' end',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 4) spaces for indentation.'])
.to eq(['Use 2 (not 4) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts an empty module body' do
Expand All @@ -345,24 +373,29 @@
inspect_source(cop,
['a = func do',
' b',
' c',
'end'])
expect(cop.messages)
.to eq(['Use 2 (not 1) spaces for indentation.'])
.to eq(['Use 2 (not 1) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'registers an offence for bad indentation of a {} body' do
inspect_source(cop,
['func {',
' b',
' c',
'}'])
expect(cop.messages)
.to eq(['Use 2 (not 3) spaces for indentation.'])
.to eq(['Use 2 (not 3) spaces for indentation.',
'Inconsistent indentation detected.'])
end

it 'accepts a correctly indented block body' do
inspect_source(cop,
['a = func do',
' b',
' c',
'end'])
expect(cop.offences).to be_empty
end
Expand Down
Loading

0 comments on commit f860b48

Please sign in to comment.