wool 0.5.1
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.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +45 -0
- data/README.rdoc +17 -0
- data/Rakefile +77 -0
- data/TODO.md +17 -0
- data/VERSION +1 -0
- data/bin/wool +4 -0
- data/features/step_definitions/wool_steps.rb +39 -0
- data/features/support/env.rb +14 -0
- data/features/support/testdata/1_input +1 -0
- data/features/support/testdata/1_output +1 -0
- data/features/support/testdata/2_input +4 -0
- data/features/support/testdata/2_output +4 -0
- data/features/support/testdata/3_input +8 -0
- data/features/support/testdata/3_output +11 -0
- data/features/support/testdata/4_input +5 -0
- data/features/support/testdata/4_output +5 -0
- data/features/wool.feature +24 -0
- data/lib/wool.rb +40 -0
- data/lib/wool/advice/advice.rb +42 -0
- data/lib/wool/advice/comment_advice.rb +37 -0
- data/lib/wool/analysis/annotations.rb +34 -0
- data/lib/wool/analysis/annotations/next_annotation.rb +26 -0
- data/lib/wool/analysis/annotations/parent_annotation.rb +20 -0
- data/lib/wool/analysis/annotations/scope_annotation.rb +37 -0
- data/lib/wool/analysis/lexical_analysis.rb +165 -0
- data/lib/wool/analysis/protocol_registry.rb +32 -0
- data/lib/wool/analysis/protocols.rb +82 -0
- data/lib/wool/analysis/scope.rb +13 -0
- data/lib/wool/analysis/sexp_analysis.rb +98 -0
- data/lib/wool/analysis/signature.rb +16 -0
- data/lib/wool/analysis/symbol.rb +10 -0
- data/lib/wool/analysis/visitor.rb +36 -0
- data/lib/wool/analysis/wool_class.rb +47 -0
- data/lib/wool/rake/task.rb +42 -0
- data/lib/wool/runner.rb +156 -0
- data/lib/wool/scanner.rb +160 -0
- data/lib/wool/support/module_extensions.rb +84 -0
- data/lib/wool/third_party/trollop.rb +845 -0
- data/lib/wool/warning.rb +145 -0
- data/lib/wool/warnings/comment_spacing.rb +30 -0
- data/lib/wool/warnings/extra_blank_lines.rb +29 -0
- data/lib/wool/warnings/extra_whitespace.rb +15 -0
- data/lib/wool/warnings/line_length.rb +113 -0
- data/lib/wool/warnings/misaligned_unindentation.rb +16 -0
- data/lib/wool/warnings/operator_spacing.rb +63 -0
- data/lib/wool/warnings/rescue_exception.rb +41 -0
- data/lib/wool/warnings/semicolon.rb +24 -0
- data/lib/wool/warnings/useless_double_quotes.rb +37 -0
- data/spec/advice_specs/advice_spec.rb +69 -0
- data/spec/advice_specs/comment_advice_spec.rb +38 -0
- data/spec/advice_specs/spec_helper.rb +1 -0
- data/spec/analysis_specs/annotations_specs/next_prev_annotation_spec.rb +47 -0
- data/spec/analysis_specs/annotations_specs/parent_annotation_spec.rb +41 -0
- data/spec/analysis_specs/annotations_specs/spec_helper.rb +5 -0
- data/spec/analysis_specs/lexical_analysis_spec.rb +179 -0
- data/spec/analysis_specs/protocol_registry_spec.rb +58 -0
- data/spec/analysis_specs/protocols_spec.rb +49 -0
- data/spec/analysis_specs/scope_spec.rb +20 -0
- data/spec/analysis_specs/sexp_analysis_spec.rb +134 -0
- data/spec/analysis_specs/spec_helper.rb +2 -0
- data/spec/analysis_specs/visitor_spec.rb +53 -0
- data/spec/analysis_specs/wool_class_spec.rb +54 -0
- data/spec/rake_specs/spec_helper.rb +1 -0
- data/spec/rake_specs/task_spec.rb +67 -0
- data/spec/runner_spec.rb +171 -0
- data/spec/scanner_spec.rb +75 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/support_specs/module_extensions_spec.rb +91 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/warning_spec.rb +95 -0
- data/spec/warning_specs/comment_spacing_spec.rb +57 -0
- data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
- data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
- data/spec/warning_specs/line_length_spec.rb +165 -0
- data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
- data/spec/warning_specs/operator_spacing_spec.rb +101 -0
- data/spec/warning_specs/rescue_exception_spec.rb +105 -0
- data/spec/warning_specs/semicolon_spec.rb +58 -0
- data/spec/warning_specs/spec_helper.rb +1 -0
- data/spec/warning_specs/useless_double_quotes_spec.rb +62 -0
- data/spec/wool_spec.rb +8 -0
- data/status_reports/2010/12/2010-12-14.md +163 -0
- data/test/third_party_tests/test_trollop.rb +1181 -0
- data/wool.gemspec +173 -0
- metadata +235 -0
data/lib/wool/warning.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
module Wool
|
2
|
+
class Warning < Struct.new(:name, :file, :body, :line_number, :severity)
|
3
|
+
extend Advice
|
4
|
+
extend ModuleExtensions
|
5
|
+
include LexicalAnalysis
|
6
|
+
include SexpAnalysis
|
7
|
+
|
8
|
+
cattr_accessor :short_name
|
9
|
+
cattr_accessor_with_default :match_filters, []
|
10
|
+
cattr_get_and_setter :severity, :short_desc, :desc
|
11
|
+
attr_accessor :settings
|
12
|
+
|
13
|
+
desc { "#{self.class.name} #{file}:#{line_number} (#{severity})" }
|
14
|
+
|
15
|
+
# This tracks all subclasses (and subclasses of subclasses, etc). Plus, this
|
16
|
+
# method is inherited, so Wool::LineWarning.all_subclasses will have all
|
17
|
+
# subclasses of Wool::LineWarning!
|
18
|
+
def self.all_warnings
|
19
|
+
@all_warnings ||= [self]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns all "concrete" warnings, that is, those that have an actual
|
23
|
+
# implementation. No meta-warnings like FileWarning/LineWarning.
|
24
|
+
#
|
25
|
+
# @return [Array<Class>] the concrete warnings you might want to use
|
26
|
+
def self.concrete_warnings
|
27
|
+
all_warnings - [self, FileWarning, LineWarning]
|
28
|
+
end
|
29
|
+
|
30
|
+
# All types should be shared and modified by *all* subclasses. This makes
|
31
|
+
# Wool::Warning.all_types a global registry.
|
32
|
+
def self.all_types
|
33
|
+
@@all_types ||= Hash.new {|h,k| h[k] = []}
|
34
|
+
end
|
35
|
+
|
36
|
+
# When a Warning subclass is subclassed, store the subclass and inform the
|
37
|
+
# next superclass up the inheritance hierarchy.
|
38
|
+
def self.inherited(klass)
|
39
|
+
self.all_warnings << klass
|
40
|
+
next_klass = self.superclass
|
41
|
+
while next_klass != Wool::Warning.superclass
|
42
|
+
next_klass.send(:inherited, klass)
|
43
|
+
next_klass = next_klass.superclass
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Override in subclasses to provide a list of options to send to Trollop
|
48
|
+
def self.options
|
49
|
+
@options ||= [:debug, "Shows debug output from wool's scanner", {:short => '-d'}]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Adds an option in Trollop format.
|
53
|
+
def self.opt(*args)
|
54
|
+
self.options << args
|
55
|
+
end
|
56
|
+
|
57
|
+
# Modified cattr_get_and_setter that updates the class's short_name and
|
58
|
+
# registers the class as a member of the given type.
|
59
|
+
def self.type(*args)
|
60
|
+
if args.any?
|
61
|
+
@type = args.first.to_s
|
62
|
+
all_types[@type] << self
|
63
|
+
self.short_name = @type[0,2].upcase + all_types[@type].size.to_s
|
64
|
+
else
|
65
|
+
@type
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Adds an instance method that extracts a key from the settings of
|
70
|
+
# the warning. This is a simple way of storing metadata about the
|
71
|
+
# discovered error/issue for presentational purposes.
|
72
|
+
def self.setting_accessor(*syms)
|
73
|
+
syms.each { |sym| class_eval("def #{sym}\n @settings[#{sym.inspect}]\nend") }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Default initializer.
|
77
|
+
def initialize(file, body, settings={})
|
78
|
+
super(self.class.short_desc, file, body, 0, self.class.severity)
|
79
|
+
@settings = settings
|
80
|
+
end
|
81
|
+
|
82
|
+
def match?(body = self.body)
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
def generated_warnings(*args)
|
87
|
+
case match_result = match?(*args)
|
88
|
+
when Array then match_result
|
89
|
+
when false, nil then []
|
90
|
+
else [self]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def fix
|
95
|
+
self.body
|
96
|
+
end
|
97
|
+
|
98
|
+
def fixable?
|
99
|
+
self.fix != self.body rescue false
|
100
|
+
end
|
101
|
+
|
102
|
+
def desc
|
103
|
+
case desc = self.class.desc
|
104
|
+
when String then desc
|
105
|
+
when Proc then instance_eval(&self.class.desc)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def indent(string, amt=nil)
|
110
|
+
amt ||= self.body.match(/^(\s*)/)[1].size
|
111
|
+
' ' * amt + string.lstrip
|
112
|
+
end
|
113
|
+
|
114
|
+
def count_occurrences(string, substring)
|
115
|
+
count = 0
|
116
|
+
0.upto(string.size - substring.size) do |start|
|
117
|
+
if string[start,substring.size] == substring
|
118
|
+
count += 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
count
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_indent(line = self.body)
|
125
|
+
line =~ /^(\s*).*$/ ? $1 : ''
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class LineWarning < Warning
|
130
|
+
alias_method :line, :body
|
131
|
+
def self.options
|
132
|
+
@options ||= []
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class FileWarning < Warning
|
137
|
+
def self.options
|
138
|
+
@options ||= []
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__), 'warnings', '**', '*.rb'))].each do |file|
|
144
|
+
load file
|
145
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Warning for insufficient space between inline comments and code
|
2
|
+
class Wool::InlineCommentSpaceWarning < Wool::LineWarning
|
3
|
+
OPTION_KEY = :inline_comment_space
|
4
|
+
DEFAULT_SPACE = 2
|
5
|
+
type :style
|
6
|
+
short_desc 'Inline comment spacing error'
|
7
|
+
desc { "Inline comments must be at least #{@settings[OPTION_KEY]} spaces from code." }
|
8
|
+
opt OPTION_KEY, 'Number of spaces between code and inline comments', :default => DEFAULT_SPACE
|
9
|
+
|
10
|
+
def match?(line = self.body)
|
11
|
+
return false unless comment_token = find_token(:on_comment)
|
12
|
+
comment_pos = comment_token.col - 1
|
13
|
+
left_of_comment = line[0..comment_pos]
|
14
|
+
stripped = left_of_comment.rstrip
|
15
|
+
return false if stripped.empty?
|
16
|
+
padding_size = left_of_comment.size - stripped.size
|
17
|
+
return spacing != padding_size
|
18
|
+
end
|
19
|
+
|
20
|
+
def fix
|
21
|
+
comment_token = find_token(:on_comment)
|
22
|
+
comment_pos = comment_token.col - 1
|
23
|
+
left_of_comment = line[0..comment_pos].rstrip
|
24
|
+
left_of_comment + (' ' * spacing) + comment_token.body
|
25
|
+
end
|
26
|
+
|
27
|
+
def spacing
|
28
|
+
@settings[OPTION_KEY] || DEFAULT_SPACE
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Warning for using semicolons outside of class declarations.
|
2
|
+
class Wool::ExtraBlankLinesWarning < Wool::FileWarning
|
3
|
+
EXTRA_LINE = /\n[\t ]*\Z/
|
4
|
+
type :style
|
5
|
+
severity 1
|
6
|
+
short_desc 'Extra blank lines'
|
7
|
+
desc { "This file has #{count_extra_lines} blank lines at the end of it." }
|
8
|
+
|
9
|
+
def match?(body = self.body)
|
10
|
+
body =~ EXTRA_LINE
|
11
|
+
end
|
12
|
+
|
13
|
+
def fix
|
14
|
+
body.gsub(/\s*\Z/, '')
|
15
|
+
end
|
16
|
+
|
17
|
+
# Counts how many extra lines there are at the end of the file.
|
18
|
+
def count_extra_lines
|
19
|
+
# We use this logic because #lines ignores blank lines at the end, and
|
20
|
+
# split(/\n/) does as well.
|
21
|
+
count = 0
|
22
|
+
working_body = self.body.dup
|
23
|
+
while working_body =~ EXTRA_LINE
|
24
|
+
working_body.sub!(EXTRA_LINE, '')
|
25
|
+
count += 1
|
26
|
+
end
|
27
|
+
count
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Warning for having extra space at the end of a line.
|
2
|
+
class Wool::ExtraWhitespaceWarning < Wool::LineWarning
|
3
|
+
type :style
|
4
|
+
severity 2
|
5
|
+
short_desc 'Extra Whitespace'
|
6
|
+
desc 'The line has trailing whitespace.'
|
7
|
+
|
8
|
+
def match?(body = self.body)
|
9
|
+
/\s+$/ === line
|
10
|
+
end
|
11
|
+
|
12
|
+
def fix
|
13
|
+
self.line.gsub(/\s+$/, '')
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class Wool::GenericLineLengthWarning < Wool::LineWarning
|
2
|
+
cattr_accessor_with_default :line_length_limit, 80000
|
3
|
+
type :style
|
4
|
+
short_desc 'Line too long'
|
5
|
+
desc { "Line length: #{line.size} > #{self.class.line_length_limit} (max)" }
|
6
|
+
|
7
|
+
def self.inspect
|
8
|
+
"Wool::GenericLineLengthWarning<#{line_length_limit}>"
|
9
|
+
end
|
10
|
+
|
11
|
+
def match?(body = self.body)
|
12
|
+
!!(line.rstrip.size > self.class.line_length_limit)
|
13
|
+
end
|
14
|
+
|
15
|
+
def fix(content_stack = nil)
|
16
|
+
result = handle_long_comments(self.line)
|
17
|
+
return result if result
|
18
|
+
result = try_to_fix_guarded_lines(self.line)
|
19
|
+
return result if result
|
20
|
+
self.line
|
21
|
+
end
|
22
|
+
|
23
|
+
def try_to_fix_guarded_lines(line)
|
24
|
+
return nil if line !~ /\b(if|unless)\s/ # quick fast check
|
25
|
+
code, guard = split_on_keyword(:if, :unless)
|
26
|
+
code.rstrip!
|
27
|
+
return nil if code.empty? || guard.empty? || code.strip == 'end'
|
28
|
+
# check guard for closing braces
|
29
|
+
return nil if count_occurrences(guard, '}') != count_occurrences(guard, '{')
|
30
|
+
indent = get_indent(line)
|
31
|
+
indent_unit = ' ' * @settings[:indent_size]
|
32
|
+
|
33
|
+
result = code
|
34
|
+
until guard.empty?
|
35
|
+
condition = indent + guard.strip
|
36
|
+
body = result.split(/\n/).map { |line| indent_unit + line}.join("\n")
|
37
|
+
new_condition, guard = split_on_keyword(condition[indent.size+1..-1], :if, :unless)
|
38
|
+
if new_condition.empty?
|
39
|
+
new_condition, guard = guard.rstrip, ''
|
40
|
+
else
|
41
|
+
new_condition = "#{condition[indent.size,1]}#{new_condition.rstrip}"
|
42
|
+
end
|
43
|
+
condition = indent + new_condition unless guard.empty?
|
44
|
+
result = condition + "\n" + body + "\n" + indent + 'end'
|
45
|
+
end
|
46
|
+
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_long_comments(line)
|
51
|
+
code, comment = split_on_token(line, :on_comment)
|
52
|
+
return nil if comment.empty?
|
53
|
+
indent, code = code.match(/^(\s*)(.*)$/)[1..2]
|
54
|
+
hashes, comment = comment.match(/^(#+\s*)(.*)$/)[1..2]
|
55
|
+
comment_cleaned = fix_long_comment(indent + hashes + comment)
|
56
|
+
code_cleaned = !code.strip.empty? ? "\n" + indent + code.rstrip : ''
|
57
|
+
comment_cleaned + code_cleaned
|
58
|
+
end
|
59
|
+
|
60
|
+
def fix_long_comment(text)
|
61
|
+
# Must have no leading text
|
62
|
+
return nil unless text =~ /^(\s*)(#+\s*)(.*)\Z/
|
63
|
+
indent, hashes, comment = $1, $2, $3
|
64
|
+
indent_size = indent.size
|
65
|
+
# The "+ 2" is (indent)#(single space)
|
66
|
+
space_for_text_per_line = self.class.line_length_limit - (indent_size + hashes.size)
|
67
|
+
lines = ['']
|
68
|
+
words = comment.split(/\s/)
|
69
|
+
quota = space_for_text_per_line
|
70
|
+
current_line = 0
|
71
|
+
while words.any?
|
72
|
+
word = words.shift
|
73
|
+
# break on word big enough to make a new line, unless its the first word
|
74
|
+
if quota - (word.size + 1) < 0 && quota < space_for_text_per_line
|
75
|
+
current_line += 1
|
76
|
+
lines << ''
|
77
|
+
quota = space_for_text_per_line
|
78
|
+
end
|
79
|
+
unless lines[current_line].empty?
|
80
|
+
lines[current_line] << ' '
|
81
|
+
quota -= 1
|
82
|
+
end
|
83
|
+
lines[current_line] << word
|
84
|
+
quota -= word.size
|
85
|
+
end
|
86
|
+
lines.map { |line| indent + hashes + line }.join("\n")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module Wool
|
91
|
+
def LineLengthCustomSeverity(size, severity)
|
92
|
+
Wool.class_eval do
|
93
|
+
if const_defined?("GenericLineLengthWarning_#{size}_#{severity}")
|
94
|
+
return const_get("GenericLineLengthWarning_#{size}_#{severity}")
|
95
|
+
end
|
96
|
+
new_warning = Class.new(Wool::GenericLineLengthWarning)
|
97
|
+
const_set("GenericLineLengthWarning_#{size}_#{severity}", new_warning)
|
98
|
+
new_warning.line_length_limit = size
|
99
|
+
new_warning.severity = severity
|
100
|
+
new_warning.desc = Wool::GenericLineLengthWarning.desc
|
101
|
+
new_warning
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def LineLengthMaximum(size)
|
106
|
+
(@table ||= {})[size] ||= LineLengthCustomSeverity(size, 8)
|
107
|
+
end
|
108
|
+
|
109
|
+
def LineLengthWarning(size)
|
110
|
+
(@table ||= {})[size] ||= LineLengthCustomSeverity(size, 3)
|
111
|
+
end
|
112
|
+
module_function :LineLengthMaximum, :LineLengthWarning, :LineLengthCustomSeverity
|
113
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This warning is used when
|
2
|
+
class Wool::MisalignedUnindentationWarning < Wool::LineWarning
|
3
|
+
type :style
|
4
|
+
severity 2
|
5
|
+
short_desc 'Misaligned Unindentation'
|
6
|
+
desc { "Expected #{@expectation} spaces, but instead found #{get_indent.size}" }
|
7
|
+
|
8
|
+
def initialize(file, line, expectation)
|
9
|
+
super(file, line)
|
10
|
+
@expectation = expectation
|
11
|
+
end
|
12
|
+
|
13
|
+
def fix
|
14
|
+
indent self.line, @expectation
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Warning for not putting space around operators
|
2
|
+
class Wool::OperatorSpacing < Wool::LineWarning
|
3
|
+
include Wool::Advice::CommentAdvice
|
4
|
+
OPERATORS = %w(+ - / * != !== = == === ~= !~ += -= *= /= ** **= ||= || && &&= &= |= | & ^)
|
5
|
+
|
6
|
+
type :style
|
7
|
+
severity 5
|
8
|
+
short_desc 'No operator spacing'
|
9
|
+
desc { "Insufficient spacing around #{self.match?(self.line).body}" }
|
10
|
+
|
11
|
+
def match?(line = self.body)
|
12
|
+
working_line = ignore_block_params line
|
13
|
+
working_line = ignore_splat_args working_line
|
14
|
+
working_line = ignore_to_proc_args working_line
|
15
|
+
working_line = ignore_array_splat_idiom working_line
|
16
|
+
lexed = lex(working_line)
|
17
|
+
lexed.each_with_index do |token, idx|
|
18
|
+
next unless token.type == :on_op
|
19
|
+
next if idx == lexed.size - 1 # Last token on line (continuation) is ok
|
20
|
+
next if token.body == '-' && [:on_float, :on_int].include?(lexed[idx+1].type)
|
21
|
+
return token if lexed[idx+1].type != :on_sp && lexed[idx+1].type != :on_op
|
22
|
+
return token if idx > 0 && ![:on_sp, :on_op].include?(lexed[idx-1].type)
|
23
|
+
end
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def generated_warnings(*args)
|
28
|
+
match?(*args) ? [self] : []
|
29
|
+
end
|
30
|
+
|
31
|
+
def ignore_block_params(line)
|
32
|
+
line.gsub(/(\{|(do))\s*\|.*\|/, '\1')
|
33
|
+
end
|
34
|
+
|
35
|
+
def ignore_splat_args(line)
|
36
|
+
line.gsub(/(\(|(, ))\&([a-z][A-Za-z0-9_]*)((, )|\)|\Z)/, '\1')
|
37
|
+
end
|
38
|
+
|
39
|
+
def ignore_array_splat_idiom(line)
|
40
|
+
line.gsub(/\[\*([a-z][A-Za-z0-9_]*)\]/, '\1')
|
41
|
+
end
|
42
|
+
|
43
|
+
def ignore_to_proc_args(line)
|
44
|
+
line.gsub(/(\(|(, ))\*([a-z][A-Za-z0-9_]*)((, )|\)|\Z)/, '\1')
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_block_line?(line)
|
48
|
+
line =~ /do\s*\|/ || line =~ /\{\s*\|/
|
49
|
+
end
|
50
|
+
|
51
|
+
def fix
|
52
|
+
line = self.line.dup
|
53
|
+
OPERATORS.each do |op|
|
54
|
+
next if op == '==' && line =~ /!==/
|
55
|
+
next if op == '=' && line =~ /!=/
|
56
|
+
next if op == '|' && self.is_block_line?(line)
|
57
|
+
embed = op.gsub(/(\+|\-|\*|\||\^)/, '\\\\\\1')
|
58
|
+
line.gsub!(/([A-Za-z0-9_]!|[A-Za-z0-9_?])(#{embed})/, '\1 \2')
|
59
|
+
line.gsub!(/(#{embed})([$A-Za-z0-9_?!])/, '\1 \2')
|
60
|
+
end
|
61
|
+
line
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Warning for rescuing "Exception" or "Object".
|
2
|
+
class Wool::RescueExceptionWarning < Wool::FileWarning
|
3
|
+
severity 5
|
4
|
+
type :dangerous
|
5
|
+
short_desc 'rescue Exception is dangerous'
|
6
|
+
desc 'The line rescues "Exception" or "Object", which is too broad. Rescue StandardError instead.'
|
7
|
+
setting_accessor :position
|
8
|
+
|
9
|
+
def match?(file = self.body)
|
10
|
+
find_sexps(:rescue).map do |_, types, name|
|
11
|
+
case types[0]
|
12
|
+
when :mrhs_new_from_args
|
13
|
+
list = types[1] + types[2..-1]
|
14
|
+
when Array
|
15
|
+
list = types
|
16
|
+
end
|
17
|
+
list.map do |type|
|
18
|
+
if type[0] == :var_ref &&
|
19
|
+
type[1][0] == :@const && type[1][1] == "Exception"
|
20
|
+
warning = RescueExceptionWarning.new(file, body, :position => type[1][2])
|
21
|
+
warning.position[0] -= 1
|
22
|
+
warning.line_number = type[1][2][1]
|
23
|
+
warning
|
24
|
+
end
|
25
|
+
end.compact
|
26
|
+
end.flatten
|
27
|
+
end
|
28
|
+
|
29
|
+
def fix
|
30
|
+
result = ""
|
31
|
+
all_lines = self.body.lines.to_a
|
32
|
+
result << all_lines[0..position[0]-1].join if position[0]-1 >= 0
|
33
|
+
result << all_lines[position[0]][0,position[1]]
|
34
|
+
result << 'StandardError'
|
35
|
+
if trailing = all_lines[position[0]][position[1] + 'Exception'.size .. -1]
|
36
|
+
result << trailing
|
37
|
+
end
|
38
|
+
result << all_lines[position[0]+1..-1].join if position[0]+1 < all_lines.size
|
39
|
+
result
|
40
|
+
end
|
41
|
+
end
|