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/.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
|