wool 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|