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/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
Copyright (c) 2009 Michael Edgar
|
2
|
+
|
3
|
+
Spirit of the License
|
4
|
+
|
5
|
+
This is an academic project, but I don't know where it will lead. I'm
|
6
|
+
licensing Wool this way in part to shield myself from liability (since
|
7
|
+
it rewrites code) and because it very well may have commercial applications
|
8
|
+
that I may wish to investigate.
|
9
|
+
|
10
|
+
Wool's License
|
11
|
+
|
12
|
+
Academic use only. No commercial or non-profit use. At least until
|
13
|
+
I know what I'm doing for sure. I couldn't find a legalese version of
|
14
|
+
an "academic use only" license, so I'll just write it again:
|
15
|
+
|
16
|
+
No non-academic use! No using Wool on code that will, at some point
|
17
|
+
in the near or distant future, produce monetary gain for you, your
|
18
|
+
relatives, your friends, your bosses, or anyone tangentially connected
|
19
|
+
to you. I don't care if you run it on legacy code that is defunct and
|
20
|
+
unused, but may have once derived you commercial gain. But don't run
|
21
|
+
it at your workplace. That's against the license.
|
22
|
+
|
23
|
+
Oh, and this important bit from the GPL applies as well:
|
24
|
+
|
25
|
+
NO WARRANTY
|
26
|
+
|
27
|
+
9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
28
|
+
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
29
|
+
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
30
|
+
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
31
|
+
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
32
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
33
|
+
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
34
|
+
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
35
|
+
REPAIR OR CORRECTION.
|
36
|
+
|
37
|
+
10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
38
|
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
39
|
+
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
40
|
+
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
41
|
+
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
42
|
+
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
43
|
+
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
44
|
+
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
45
|
+
POSSIBILITY OF SUCH DAMAGES.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= wool
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Michael Edgar. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "wool"
|
8
|
+
gem.summary = %Q{Analysis and Linting tool for Ruby.}
|
9
|
+
gem.description = %Q{Wool is an advanced static analysis tool for Ruby.}
|
10
|
+
gem.email = "michael.j.edgar@dartmouth.edu"
|
11
|
+
gem.homepage = "http://github.com/michaeledgar/wool"
|
12
|
+
gem.authors = ["Michael Edgar"]
|
13
|
+
gem.add_dependency "ruby_parser", ">= 2.0.5"
|
14
|
+
gem.add_dependency "ruby2ruby", ">= 1.2.4"
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "yard", ">= 0"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
task :rcov => :default
|
31
|
+
|
32
|
+
require 'rake/testtask'
|
33
|
+
Rake::TestTask.new do |t|
|
34
|
+
t.libs << "test"
|
35
|
+
t.test_files = FileList['test/**/test*.rb']
|
36
|
+
t.verbose = true
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'cucumber'
|
40
|
+
require 'cucumber/rake/task'
|
41
|
+
|
42
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
43
|
+
t.cucumber_opts = "features --format pretty"
|
44
|
+
end
|
45
|
+
|
46
|
+
if true
|
47
|
+
begin
|
48
|
+
require 'wool'
|
49
|
+
Wool::Rake::WoolTask.new(:wool) do |wool|
|
50
|
+
wool.libs << 'lib' << 'spec'
|
51
|
+
wool.using << :all << Wool::LineLengthMaximum(100) << Wool::LineLengthWarning(80)
|
52
|
+
wool.options = '--debug --fix'
|
53
|
+
wool.fix << Wool::ExtraBlankLinesWarning << Wool::ExtraWhitespaceWarning << Wool::LineLengthWarning(80)
|
54
|
+
end
|
55
|
+
rescue LoadError => err
|
56
|
+
task :wool do
|
57
|
+
abort 'Wool is not available. In order to run wool, you must: sudo gem install wool'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
task :rebuild => [:gemspec, :build, :install] do
|
63
|
+
%x(rake wool)
|
64
|
+
end
|
65
|
+
|
66
|
+
task :spec => :check_dependencies
|
67
|
+
|
68
|
+
task :default => [:spec, :test]
|
69
|
+
|
70
|
+
begin
|
71
|
+
require 'yard'
|
72
|
+
YARD::Rake::YardocTask.new
|
73
|
+
rescue LoadError
|
74
|
+
task :yardoc do
|
75
|
+
abort 'YARD is not available. In order to run yardoc, you must: sudo gem install yard'
|
76
|
+
end
|
77
|
+
end
|
data/TODO.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Cleanup
|
2
|
+
|
3
|
+
* Completely remove context stack remnants
|
4
|
+
|
5
|
+
# Useful Features
|
6
|
+
|
7
|
+
* Easy, DSL-like way to specify regex-matching, token-matching for match?
|
8
|
+
* Easy, DSL-like way to specify gsub-based fix calls
|
9
|
+
* Token Matching engine. Something along the lines of being able to match: [!whitespace, whitespace?, comment] to match code, possibly some whitespace, and a comment. Could donate to YARD.
|
10
|
+
* * DFA? Simple backtracking recursion?
|
11
|
+
* * Leftmost-longest
|
12
|
+
|
13
|
+
# Useful Warnings
|
14
|
+
|
15
|
+
* Assignment in condition
|
16
|
+
* Multiline blocks with { } notation
|
17
|
+
* Never-executed code
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.1
|
data/bin/wool
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
TESTDATA_DIR = File.join(File.dirname(__FILE__), '..', 'support', 'testdata')
|
2
|
+
|
3
|
+
Given(/the following inputs and outputs:/) do |table|
|
4
|
+
@table = table
|
5
|
+
end
|
6
|
+
|
7
|
+
When(/I scan for warnings/) do
|
8
|
+
@result_table = [ ['input', 'output'] ]
|
9
|
+
swizzling_io do
|
10
|
+
@table.hashes.each do |hash|
|
11
|
+
full_path = File.join(TESTDATA_DIR, hash[:input])
|
12
|
+
runner = Wool::Scanner.new({})
|
13
|
+
warnings = runner.scan(File.read(full_path), hash[:input])
|
14
|
+
@result_table << [hash[:input], warnings.size.to_s]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Then(/the input and output tables should match/) do
|
20
|
+
@table.diff!(@result_table)
|
21
|
+
end
|
22
|
+
|
23
|
+
When(/I scan-and-fix warnings/) do
|
24
|
+
@result_table = [ ['input', 'output'] ]
|
25
|
+
swizzling_io do
|
26
|
+
@table.hashes.each do |hash|
|
27
|
+
full_path = File.join(TESTDATA_DIR, hash[:input])
|
28
|
+
expected_output = File.read(File.join(TESTDATA_DIR, hash[:output]))
|
29
|
+
captured_output = StringIO.new
|
30
|
+
Wool::LineLengthMaximum(80)
|
31
|
+
runner = Wool::Scanner.new(:fix => true, :output_file => captured_output)
|
32
|
+
runner.scan(File.read(full_path), hash[:input])
|
33
|
+
reported_output = hash[:output]
|
34
|
+
reported_output += '+' if captured_output.string != expected_output
|
35
|
+
STDERR.puts [captured_output.string, expected_output].inspect if captured_output.string != expected_output
|
36
|
+
@result_table << [hash[:input], reported_output]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
2
|
+
require 'wool'
|
3
|
+
|
4
|
+
require 'spec/expectations'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
def swizzling_io
|
8
|
+
old_stdout, $stdout = $stdout, StringIO.new
|
9
|
+
yield
|
10
|
+
$stdout.string
|
11
|
+
ensure
|
12
|
+
$stdout = old_stdout
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
a +b
|
@@ -0,0 +1 @@
|
|
1
|
+
a + b
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Find Warnings
|
2
|
+
In order to clean code
|
3
|
+
A user should be able to
|
4
|
+
scan for warnings
|
5
|
+
|
6
|
+
Scenario: Scan Single File
|
7
|
+
Given the following inputs and outputs:
|
8
|
+
| input | output |
|
9
|
+
| 1_input | 1 |
|
10
|
+
| 2_input | 4 |
|
11
|
+
| 3_input | 6 |
|
12
|
+
| 4_input | 3 |
|
13
|
+
When I scan for warnings
|
14
|
+
Then the input and output tables should match
|
15
|
+
|
16
|
+
Scenario: Scan-n-fix Single File
|
17
|
+
Given the following inputs and outputs:
|
18
|
+
| input | output |
|
19
|
+
| 1_input | 1_output |
|
20
|
+
| 2_input | 2_output |
|
21
|
+
| 3_input | 3_output |
|
22
|
+
| 4_input | 4_output |
|
23
|
+
When I scan-and-fix warnings
|
24
|
+
Then the input and output tables should match
|
data/lib/wool.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Dependencies
|
2
|
+
require 'ripper'
|
3
|
+
require 'wool/third_party/trollop'
|
4
|
+
require 'wool/support/module_extensions'
|
5
|
+
require 'wool/advice/advice'
|
6
|
+
require 'wool/analysis/lexical_analysis'
|
7
|
+
require 'wool/analysis/sexp_analysis'
|
8
|
+
require 'wool/analysis/visitor'
|
9
|
+
require 'wool/analysis/symbol'
|
10
|
+
require 'wool/analysis/protocols'
|
11
|
+
require 'wool/analysis/signature'
|
12
|
+
require 'wool/analysis/wool_class'
|
13
|
+
require 'wool/analysis/protocol_registry'
|
14
|
+
require 'wool/analysis/scope'
|
15
|
+
require 'wool/analysis/annotations'
|
16
|
+
require 'wool/advice/comment_advice'
|
17
|
+
|
18
|
+
module Wool
|
19
|
+
# MOVE THIS
|
20
|
+
# TODO(adgar): move this to someplace effing sensible
|
21
|
+
def self.initialize_global_scope
|
22
|
+
object_class = SexpAnalysis::WoolClass.new('Object', nil)
|
23
|
+
global = SexpAnalysis::Scope.new(nil, object_class.class_object, {'Object' => object_class})
|
24
|
+
SexpAnalysis::Scope.const_set("GlobalScope", global) unless SexpAnalysis.const_defined?("GlobalScope")
|
25
|
+
object_class.instance_variable_set("@scope", SexpAnalysis::Scope::GlobalScope)
|
26
|
+
module_class = SexpAnalysis::WoolClass.new('Module')
|
27
|
+
module_class.superclass = object_class
|
28
|
+
end
|
29
|
+
initialize_global_scope
|
30
|
+
end
|
31
|
+
# Runners
|
32
|
+
require 'wool/runner'
|
33
|
+
require 'wool/rake/task'
|
34
|
+
# Program logic
|
35
|
+
require 'wool/warning'
|
36
|
+
require 'wool/scanner'
|
37
|
+
|
38
|
+
module Wool
|
39
|
+
VERSION = "0.5.0"
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Wool
|
2
|
+
# This module provides other modules the ability to add advice
|
3
|
+
# to methods. This module makes use of these functions opt-in.
|
4
|
+
module Advice
|
5
|
+
def advice_counter
|
6
|
+
@advice_counter ||= 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def bump_advice_counter!
|
10
|
+
@advice_counter += 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def before_advice(meth, advice)
|
14
|
+
with_advice(meth, :before => proc { send(advice) })
|
15
|
+
end
|
16
|
+
|
17
|
+
def after_advice(meth, advice)
|
18
|
+
with_advice(meth, :after => proc { send(advice) })
|
19
|
+
end
|
20
|
+
|
21
|
+
def argument_advice(meth, argument_tweaker)
|
22
|
+
with_advice(meth, :args => argument_tweaker)
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_advice(meth, settings)
|
26
|
+
counter = advice_counter
|
27
|
+
alias_method "#{meth}_old#{counter}".to_sym, meth
|
28
|
+
define_method meth do |*args|
|
29
|
+
identity = proc {|*x| x}
|
30
|
+
instance_eval(&(settings[:before] || identity))
|
31
|
+
if settings[:args]
|
32
|
+
new_args = instance_eval(& proc { send(settings[:args], *args)})
|
33
|
+
end
|
34
|
+
result = send("#{meth}_old#{counter}", *new_args)
|
35
|
+
instance_eval(&(settings[:after] || identity))
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
bump_advice_counter!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Wool
|
2
|
+
module Advice
|
3
|
+
# Using this module, you can make your match? method automatically
|
4
|
+
# receive de-commented source text.
|
5
|
+
#
|
6
|
+
# class MyWarning < Wool::Warning do
|
7
|
+
# extend Wool::Advice::CommentAdvice
|
8
|
+
#
|
9
|
+
# def self.match?(body, context)
|
10
|
+
# body.include?('#')
|
11
|
+
# end
|
12
|
+
# remove_comments
|
13
|
+
# end
|
14
|
+
module CommentAdvice
|
15
|
+
def self.included(klass)
|
16
|
+
klass.__send__(:extend, ClassMethods)
|
17
|
+
klass.__send__(:include, InstanceMethods)
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def remove_comments
|
22
|
+
argument_advice :match?, :comment_removing_twiddler
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module InstanceMethods
|
27
|
+
# This twiddler aims to remove comments and trailing whitespace
|
28
|
+
# from the ruby source input, so that warnings that aren't concerned
|
29
|
+
# with the implications of comments in their source can safely
|
30
|
+
# discard them. Uses Ripper to look for comment tokens.
|
31
|
+
def comment_removing_twiddler(body = self.body, settings = {})
|
32
|
+
[split_on_token(body, :on_comment).first.rstrip, settings]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|