Skip to content
This repository has been archived by the owner on Aug 1, 2020. It is now read-only.

Add ruler for unnecessary double quotes, refs #91. #150

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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