Skip to content

Commit

Permalink
Add ruler for unnecessary double quotes, refs turboladen#91.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Crump committed Sep 30, 2013
1 parent 808d087 commit 15e6313
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 55 deletions.
2 changes: 1 addition & 1 deletion features/valid_ruby.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Feature: Valid Ruby
Given a file named "extra_end.rb" with:
"""
def a_method
puts "stuff"
puts 'stuff'
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/tailor/cli/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ def self.parse!(args)
options.style[:allow_trailing_line_spaces] = c
end

opt.on('--allow-unnecessary-double-quotes BOOL',
'Check for unnecessary use of double quotes?',
'(default: false)') do |c|
options.style[:allow_unnecessary_double_quotes] = c
end

opt.on('--indentation-spaces NUMBER', INTEGER_OR_OFF,
'Spaces to expect indentation. (default: 2)') do |c|
options.style[:indentation_spaces] = c
Expand Down
2 changes: 2 additions & 0 deletions lib/tailor/configuration/style.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def self.define_property(name)
define_property :allow_hard_tabs
define_property :allow_screaming_snake_case_classes
define_property :allow_trailing_line_spaces
define_property :allow_unnecessary_double_quotes
define_property :allow_invalid_ruby
define_property :indentation_spaces
define_property :max_code_lines_in_class
Expand All @@ -53,6 +54,7 @@ def initialize
allow_hard_tabs(false, level: :error)
allow_screaming_snake_case_classes(false, level: :error)
allow_trailing_line_spaces(false, level: :error)
allow_unnecessary_double_quotes(false, level: :warn)
allow_invalid_ruby(false, level: :warn)
indentation_spaces(2, level: :error)
max_code_lines_in_class(300, level: :error)
Expand Down
63 changes: 63 additions & 0 deletions lib/tailor/rulers/allow_unnecessary_double_quotes_ruler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require_relative '../ruler'

class Tailor
module Rulers
class AllowUnnecessaryDoubleQuotesRuler < Tailor::Ruler

def initialize(config, options)
super(config, options)
add_lexer_observers :nl
end

def nl_update(lexed_line, lineno, column)
quotes(lexed_line).each do |quote|
unless contains_embedded_expression?(quote) ||
contains_escape_sequence?(quote)
measure(lineno, column(quote.first))
end
end
end

# Checks to see if the double_quotes are unnecessary.
#
# @param [Fixnum] lineno Line the problem was found on.
# @param [Fixnum] column Column the problem was found on.
def measure(lineno, column)
@problems << Problem.new('unnecessary_double_quotes', lineno, column,
"Unnecessary double quotes at column #{column}, " +
'expected single quotes.', @options[:level])
end

private

def contains_embedded_expression?(tokens)
tokens.any? { |t| t[1] == :on_embexpr_beg }
end

def contains_escape_sequence?(tokens)
tokens.any? do |t|
t[1] == :on_tstring_content and t[2].match(/\\[a-z]+/)
end
end

def quotes(tokens)
tokens.select do |t|
true if (double_quote_start?(t))..(double_quote_end?(t))
end.slice_before { |t| double_quote_start?(t) }.reject { |q| q.empty? }
end

def column(token)
token[0][1]
end

def double_quote_start?(token)
token[1] == :on_tstring_beg and token[2] == '"'
end

def double_quote_end?(token)
token[1] == :on_tstring_end and token[2] == '"'
end

end
end
end
8 changes: 4 additions & 4 deletions spec/functional/horizontal_spacing/hard_tabs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

HARD_TABS['hard_tab'] =
%Q{def something
\tputs "something"
\tputs 'something'
end}

HARD_TABS['hard_tab_with_spaces'] =
%Q{class Thing
def something
\t puts "something"
\t puts 'something'
end
end}

Expand All @@ -27,14 +27,14 @@ def something
HARD_TABS['hard_tab_with_1_indented_space'] =
%Q{class Thing
def something
\t puts "something"
\t puts 'something'
end
end}

HARD_TABS['hard_tab_with_2_indented_spaces'] =
%Q{class Thing
def something
\t puts "something"
\t puts 'something'
end
end}

Expand Down
97 changes: 97 additions & 0 deletions spec/functional/string_quoting_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require 'spec_helper'
require_relative '../support/string_quoting_cases'
require 'tailor/critic'
require 'tailor/configuration/style'

describe 'String Quoting' do

def file_name
self.class.description
end

def contents
QUOTING[file_name] || begin
raise "Example not found: #{file_name}"
end
end

before do
Tailor::Logger.stub(:log)
FakeFS.activate!
FileUtils.touch file_name
File.open(file_name, 'w') { |f| f.write contents }
end

let(:critic) { Tailor::Critic.new }

let(:style) do
style = Tailor::Configuration::Style.new
style.trailing_newlines 0, level: :off
style.allow_invalid_ruby true, level: :off
style
end

context :single_quotes_no_interpolation do
it 'does not warn' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to be_empty
end
end

context :double_quotes_with_interpolation do
it 'does not warn' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to be_empty
end
end

context :double_quotes_no_interpolation do
it 'warns that double quotes are unnecessary' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to eql [{
:type => 'unnecessary_double_quotes',
:line => 1,
:column => 6,
:message => 'Unnecessary double quotes at column 6, expected single quotes.',
:level => :warn
}]
end
end

context :double_quotes_no_interpolation_twice do
it 'warns that double quotes are unnecessary' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to eql [
{
:type => 'unnecessary_double_quotes',
:line => 1,
:column => 6,
:message => 'Unnecessary double quotes at column 6, expected single quotes.',
:level => :warn
},
{
:type => 'unnecessary_double_quotes',
:line => 1,
:column => 14,
:message => 'Unnecessary double quotes at column 14, expected single quotes.',
:level => :warn
}
]
end
end

context :nested_quotes do
it 'does not warn' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to be_empty
end
end

context :escape_sequence do
it 'does not warn when a double quoted string contains a newline' do
critic.check_file(file_name, style.to_hash)
expect(critic.problems[file_name]).to be_empty
end
end

end
2 changes: 1 addition & 1 deletion spec/functional/vertical_spacing/class_length_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
include Pizza
def barrel_roll
puts "DOABARRELROLL!"
puts 'DOABARRELROLL!'
end
end}

Expand Down
40 changes: 20 additions & 20 deletions spec/support/bad_indentation_cases.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@

INDENT_1['def_content_indented_end'] =
%Q{def a
puts "stuff"
puts 'stuff'
end}

INDENT_1['class_def_content_outdented_end'] =
%Q{class A
def a
puts "stuff"
puts 'stuff'
end
end}

INDENT_1['class_def_outdented_content'] =
%Q{class A
def a
puts "stuff"
puts 'stuff'
end
end}

Expand All @@ -56,85 +56,85 @@ def self.my_method
%Q{def my_method
case true
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['case_indented_whens_level_trailing_comment'] =
%Q{def my_method
case true # comment
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['case_outdented_whens_level'] =
%Q{def my_method
case true
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['case_when_indented_whens_level'] =
%Q{def my_method
case true
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['case_when_outdented_whens_level'] =
%Q{def my_method
case true
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['case_indented_whens_in'] =
%Q{def my_method
case true
when true
puts "stuff"
puts 'stuff'
when false
puts "blah blah"
puts 'blah blah'
end
end}

INDENT_1['while_do_indented'] =
%Q{ while true do
puts "something"
puts 'something'
end}

INDENT_1['while_do_outdented'] =
%Q{def my_method
while true do
puts "something"
puts 'something'
end
end}

INDENT_1['while_do_content_outdented'] =
%Q{def my_method
while true do
puts "something"
puts 'something'
end
end}

INDENT_1['while_do_content_indented'] =
%Q{def my_method
while true do
puts "something"
puts 'something'
end
end}

Expand Down Expand Up @@ -240,7 +240,7 @@ def self.my_method
'/profiles/DVR5000/ssdp_notification.erb'}

INDENT_1['multi_line_method_call_end_in'] =
%Q{def initialize(raw_response)
%q{def initialize(raw_response)
if raw_response.nil? || raw_response.empty?
raise RTSP::Error,
"#{self.class} received nil string--this shouldn't happen."
Expand Down
Loading

0 comments on commit 15e6313

Please sign in to comment.