reek 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/History.txt +20 -0
  2. data/README.txt +4 -80
  3. data/Rakefile +15 -4
  4. data/bin/reek +10 -16
  5. data/config/defaults.reek +53 -0
  6. data/lib/reek.rb +1 -21
  7. data/lib/reek/block_context.rb +37 -0
  8. data/lib/reek/class_context.rb +73 -0
  9. data/lib/reek/code_context.rb +47 -0
  10. data/lib/reek/code_parser.rb +204 -0
  11. data/lib/reek/exceptions.reek +13 -0
  12. data/lib/reek/if_context.rb +25 -0
  13. data/lib/reek/method_context.rb +85 -0
  14. data/lib/reek/module_context.rb +34 -0
  15. data/lib/reek/name.rb +42 -0
  16. data/lib/reek/object_refs.rb +3 -6
  17. data/lib/reek/options.rb +60 -40
  18. data/lib/reek/rake_task.rb +20 -29
  19. data/lib/reek/report.rb +16 -27
  20. data/lib/reek/sexp_formatter.rb +52 -0
  21. data/lib/reek/singleton_method_context.rb +27 -0
  22. data/lib/reek/smell_warning.rb +49 -0
  23. data/lib/reek/smells/control_couple.rb +21 -13
  24. data/lib/reek/smells/duplication.rb +23 -27
  25. data/lib/reek/smells/feature_envy.rb +18 -25
  26. data/lib/reek/smells/large_class.rb +32 -17
  27. data/lib/reek/smells/long_method.rb +24 -16
  28. data/lib/reek/smells/long_parameter_list.rb +25 -18
  29. data/lib/reek/smells/long_yield_list.rb +7 -9
  30. data/lib/reek/smells/nested_iterators.rb +13 -9
  31. data/lib/reek/smells/smell_detector.rb +66 -0
  32. data/lib/reek/smells/smells.rb +71 -10
  33. data/lib/reek/smells/uncommunicative_name.rb +49 -41
  34. data/lib/reek/smells/utility_function.rb +18 -18
  35. data/lib/reek/source.rb +116 -0
  36. data/lib/reek/spec.rb +146 -0
  37. data/lib/reek/stop_context.rb +62 -0
  38. data/lib/reek/yield_call_context.rb +14 -0
  39. data/reek.gemspec +42 -0
  40. data/spec/integration/reek_source_spec.rb +20 -0
  41. data/spec/{script_spec.rb → integration/script_spec.rb} +11 -24
  42. data/spec/reek/class_context_spec.rb +198 -0
  43. data/spec/reek/code_context_spec.rb +92 -0
  44. data/spec/reek/code_parser_spec.rb +44 -0
  45. data/spec/reek/config_spec.rb +42 -0
  46. data/spec/reek/if_context_spec.rb +17 -0
  47. data/spec/reek/method_context_spec.rb +52 -0
  48. data/spec/reek/module_context_spec.rb +38 -0
  49. data/spec/reek/options_spec.rb +2 -28
  50. data/spec/reek/report_spec.rb +6 -40
  51. data/spec/reek/sexp_formatter_spec.rb +31 -0
  52. data/spec/reek/singleton_method_context_spec.rb +17 -0
  53. data/spec/reek/smells/control_couple_spec.rb +10 -18
  54. data/spec/reek/smells/duplication_spec.rb +53 -32
  55. data/spec/reek/smells/feature_envy_spec.rb +87 -49
  56. data/spec/reek/smells/large_class_spec.rb +45 -4
  57. data/spec/reek/smells/long_method_spec.rb +25 -41
  58. data/spec/reek/smells/long_parameter_list_spec.rb +30 -76
  59. data/spec/reek/smells/nested_iterators_spec.rb +19 -29
  60. data/spec/reek/smells/smell_spec.rb +9 -18
  61. data/spec/reek/smells/uncommunicative_name_spec.rb +88 -53
  62. data/spec/reek/smells/utility_function_spec.rb +45 -44
  63. data/spec/samples/inline_spec.rb +40 -0
  64. data/spec/samples/optparse_spec.rb +100 -0
  65. data/spec/samples/redcloth_spec.rb +93 -0
  66. data/spec/spec_helper.rb +3 -1
  67. data/tasks/reek.rake +1 -10
  68. data/tasks/rspec.rake +16 -35
  69. metadata +43 -46
  70. data/lib/reek/checker.rb +0 -66
  71. data/lib/reek/class_checker.rb +0 -25
  72. data/lib/reek/file_checker.rb +0 -20
  73. data/lib/reek/method_checker.rb +0 -198
  74. data/lib/reek/printer.rb +0 -154
  75. data/lib/reek/smells/smell.rb +0 -56
  76. data/lib/reek/version.rb +0 -9
  77. data/setup.rb +0 -1585
  78. data/spec/integration_spec.rb +0 -30
  79. data/spec/reek/class_checker_spec.rb +0 -48
  80. data/spec/reek/method_checker_spec.rb +0 -67
  81. data/spec/reek/printer_spec.rb +0 -30
  82. data/spec/reek_source_spec.rb +0 -12
  83. data/spec/samples/inline.reek +0 -27
  84. data/spec/samples/optparse.reek +0 -79
  85. data/spec/samples/optparse/date.rb +0 -17
  86. data/spec/samples/optparse/shellwords.rb +0 -6
  87. data/spec/samples/optparse/time.rb +0 -10
  88. data/spec/samples/optparse/uri.rb +0 -6
  89. data/spec/samples/optparse/version.rb +0 -70
  90. data/spec/samples/redcloth.reek +0 -65
  91. data/tasks/samples.rake +0 -17
  92. data/website/index.html +0 -71
  93. data/website/index.txt +0 -40
  94. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  95. data/website/stylesheets/screen.css +0 -138
  96. data/website/template.rhtml +0 -48
data/History.txt CHANGED
@@ -1,3 +1,23 @@
1
+ == 1.0.0 2009-04-05
2
+
3
+ === Major enhancements:
4
+
5
+ * Use *.reek files in source tree to configure Reek's behaviour
6
+ * Added -f option to configure report format
7
+ * --sort_order replaced by -f, -c and -s
8
+ * Matchers provided for rspec; eg. foo.should_not reek
9
+
10
+ === Minor enhancements:
11
+
12
+ * Smells in singleton methods are now analysed
13
+ * Uncommunicative parameter names in blocks now reported
14
+ * Modules and blocks now reflected in scope of smell reports
15
+
16
+ === Fixes:
17
+
18
+ * Corrected false reports of long arg lists to yield
19
+ * A method can now be a UtilityFunction only when it includes a call
20
+
1
21
  == 0.3.1 2008-11-17
2
22
 
3
23
  * Minor enhancements:
data/README.txt CHANGED
@@ -1,82 +1,6 @@
1
- = reek
1
+ = Reek
2
2
 
3
- * http://rubyforge.org/projects/reek/
4
- * mailto:kevin@rutherford-software.com
3
+ Code smell detection for Ruby.
5
4
 
6
- == DESCRIPTION:
7
-
8
- Reek is a tool that examines Ruby classes, modules and methods and
9
- reports any code smells it finds.
10
-
11
- === SUPPORTED CODE SMELLS:
12
-
13
- Reek currently includes very naive checks for the following smells:
14
-
15
- * Long Method
16
- * Large Class
17
- * Feature Envy
18
- * Uncommunicative Name
19
- * Long Parameter List
20
- * Utility Function
21
- * Nested Iterators
22
- * Control Couple
23
- * Duplication
24
-
25
- == FEATURES/PROBLEMS:
26
-
27
- * Most of the current checks are quite naive.
28
- * Not many smells checked right now; more coming soon.
29
- * The current Feature Envy check is probably over zealous.
30
- * There's no convenient programmer's API just yet.
31
-
32
- == SYNOPSIS:
33
-
34
- $ reek [options] sources
35
-
36
- (See `reek --help` for details.)
37
-
38
- == DEPENDENCIES:
39
-
40
- Reek makes use of the following other gems:
41
-
42
- * ParseTree
43
- * sexp_processor
44
-
45
- == INSTALL:
46
-
47
- Get the latest version of the gem:
48
-
49
- $ gem install reek
50
-
51
- Or get the latest source code from:
52
-
53
- $ git clone git://github.com/kevinrutherford/reek.git
54
-
55
- or
56
-
57
- $ git clone git://rubyforge.org/reek.git
58
-
59
- == LICENSE:
60
-
61
- (The MIT License)
62
-
63
- Copyright (c) 2008 Kevin Rutherford, Rutherford Software Ltd
64
-
65
- Permission is hereby granted, free of charge, to any person obtaining
66
- a copy of this software and associated documentation files (the
67
- 'Software'), to deal in the Software without restriction, including
68
- without limitation the rights to use, copy, modify, merge, publish,
69
- distribute, sublicense, and/or sell copies of the Software, and to
70
- permit persons to whom the Software is furnished to do so, subject to
71
- the following conditions:
72
-
73
- The above copyright notice and this permission notice shall be
74
- included in all copies or substantial portions of the Software.
75
-
76
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
77
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
80
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
81
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
82
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5
+ The documentation is at http://wiki.github.com/kevinrutherford/reek
6
+ The code lives at http://github.com/kevinrutherford/reek/tree
data/Rakefile CHANGED
@@ -1,6 +1,17 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
1
+ require 'rake'
2
+ require 'rake/clean'
3
3
 
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
4
+ $:.unshift File.dirname(__FILE__) + '/lib'
5
5
 
6
- task :default => :spec
6
+ PROJECT_NAME = 'reek'
7
+
8
+ BUILD_DIR = 'build'; directory BUILD_DIR
9
+ PKG_DIR = "#{BUILD_DIR}/pkg"; directory PKG_DIR
10
+ RDOC_DIR = "#{BUILD_DIR}/rdoc"; directory RDOC_DIR
11
+
12
+ GEM_MANIFEST = "Manifest.txt"
13
+ VERSION_FILE = 'lib/reek.rb'
14
+
15
+ CLOBBER.include("#{BUILD_DIR}/*")
16
+
17
+ Dir['tasks/**/*.rake'].each { |t| load t }
data/bin/reek CHANGED
@@ -1,25 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Created on 2008-2-17.
4
- # Copyright (c) 2008 Kevin Rutherford, Rutherford Software Ltd. All rights reserved.
3
+ # Reek examines Ruby source code for smells.
4
+ # Visit http://reek.rubyforge.org/ for docs etc.
5
+ #
6
+ # Author: Kevin Rutherford
5
7
  #
6
8
 
7
- require File.join(File.dirname(__FILE__), '../lib/', 'reek')
8
- require File.join(File.dirname(__FILE__), '../lib/', 'reek/options')
9
+ require 'reek'
10
+ require 'reek/source'
11
+ require 'reek/options'
9
12
 
10
- sources = Reek::Options.parse(ARGV)
11
13
  exitstatus = 0
12
- sources.each do |src|
13
- smells = Reek.analyse(src)
14
- next if smells.empty?
15
- if sources.size == 1
16
- puts smells.to_s
17
- else
18
- puts "\"#{src}\" -- #{smells.length} warnings:"
19
- puts smells.to_s
20
- puts
21
- end
14
+ source = Reek::Options.parse(ARGV)
15
+ if source.smelly?
16
+ puts source.report
22
17
  exitstatus = 2
23
18
  end
24
-
25
19
  exit(exitstatus)
@@ -0,0 +1,53 @@
1
+ ---
2
+ LargeClass:
3
+ max_methods: 25
4
+ exclude:
5
+ - Array
6
+ - Hash
7
+ - Module
8
+ - String
9
+ enabled: true
10
+ LongParameterList:
11
+ max_params: 3
12
+ exclude: []
13
+
14
+ enabled: true
15
+ FeatureEnvy:
16
+ exclude:
17
+ - initialize
18
+ enabled: true
19
+ UncommunicativeName:
20
+ accept:
21
+ - Inline::C
22
+ exclude: []
23
+
24
+ enabled: true
25
+ reject:
26
+ - !ruby/regexp /^.[0-9]*$/
27
+ NestedIterators:
28
+ exclude: []
29
+
30
+ enabled: true
31
+ LongMethod:
32
+ max_statements: 5
33
+ exclude:
34
+ - initialize
35
+ enabled: true
36
+ Duplication:
37
+ exclude: []
38
+
39
+ enabled: true
40
+ max_calls: 1
41
+ UtilityFunction:
42
+ exclude: []
43
+
44
+ enabled: true
45
+ ControlCouple:
46
+ exclude:
47
+ - initialize
48
+ enabled: true
49
+ LongYieldList:
50
+ max_params: 3
51
+ exclude: []
52
+
53
+ enabled: true
data/lib/reek.rb CHANGED
@@ -1,25 +1,5 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
- require 'reek/file_checker'
4
- require 'reek/report'
5
-
6
3
  module Reek # :doc:
7
-
8
- #
9
- # Analyse the given source, looking for code smells.
10
- # The source can be a filename or a String containing Ruby code.
11
- # Returns a +Report+ listing the smells found.
12
- #
13
- def self.analyse(src) # :doc:
14
- report = Report.new
15
- source = Reek.get_source(src)
16
- FileChecker.new(report).check_source(source)
17
- report
18
- end
19
-
20
- private
21
-
22
- def self.get_source(src)
23
- File.exists?(src) ? IO.readlines(src).join : src
24
- end
4
+ VERSION = '1.0.0'
25
5
  end
@@ -0,0 +1,37 @@
1
+ require 'reek/code_context'
2
+
3
+ module Reek
4
+ class BlockContext < CodeContext
5
+
6
+ def initialize(outer, exp)
7
+ super
8
+ @parameters = []
9
+ @local_variables = []
10
+ @name = Name.new('block')
11
+ end
12
+
13
+ def inside_a_block?
14
+ true
15
+ end
16
+
17
+ def has_parameter(name)
18
+ @parameters.include?(name) or @outer.has_parameter(name)
19
+ end
20
+
21
+ def nested_block?
22
+ @outer.inside_a_block?
23
+ end
24
+
25
+ def record_parameter(sym)
26
+ @parameters << Name.new(sym)
27
+ end
28
+
29
+ def outer_name
30
+ "#{@outer.outer_name}#{@name}/"
31
+ end
32
+
33
+ def variable_names
34
+ @parameters + @local_variables
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ require 'reek/code_context'
2
+
3
+ class Class
4
+ def non_inherited_methods
5
+ instance_methods(false) + private_instance_methods(false)
6
+ end
7
+
8
+ def is_overriding_method?(sym)
9
+ instance_methods(false).include?(sym) and superclass.instance_methods(true).include?(sym)
10
+ end
11
+ end
12
+
13
+ module Reek
14
+ class ClassContext < CodeContext
15
+
16
+ def ClassContext.create(outer, exp)
17
+ res = Name.resolve(exp[1], outer)
18
+ ClassContext.new(res[0], res[1], exp[2])
19
+ end
20
+
21
+ def initialize(outer, name, superclass = nil)
22
+ super(outer, nil)
23
+ @name = name
24
+ @superclass = superclass
25
+ @parsed_methods = []
26
+ @instance_variables = []
27
+ end
28
+
29
+ def myself
30
+ @myself ||= @outer.find_module(@name)
31
+ end
32
+
33
+ def find_module(modname)
34
+ sym = modname.to_s
35
+ return nil unless myself
36
+ myself.const_defined?(sym) ? myself.const_get(sym) : nil
37
+ end
38
+
39
+ def is_overriding_method?(name)
40
+ return false unless myself
41
+ myself.is_overriding_method?(name.to_s)
42
+ end
43
+
44
+ def is_struct?
45
+ @superclass == [:const, :Struct]
46
+ end
47
+
48
+ def num_methods
49
+ meths = myself ? myself.non_inherited_methods : @parsed_methods
50
+ meths.length
51
+ end
52
+
53
+ def record_instance_variable(sym)
54
+ @instance_variables << Name.new(sym)
55
+ end
56
+
57
+ def record_method(name)
58
+ @parsed_methods << name.to_s
59
+ end
60
+
61
+ def outer_name
62
+ "#{@outer.outer_name}#{@name}#"
63
+ end
64
+
65
+ def to_s
66
+ "#{@outer.outer_name}#{@name}"
67
+ end
68
+
69
+ def variable_names
70
+ @instance_variables
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ module Reek
2
+
3
+ #
4
+ # Superclass for all types of source code context. Each instance represents
5
+ # a code element of some kind, and each provides behaviour relevant to that
6
+ # code element. CodeContexts form a tree in the same way the code does,
7
+ # with each context holding a reference to a unique outer context.
8
+ #
9
+ class CodeContext
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(outer, exp)
14
+ @outer = outer
15
+ @exp = exp
16
+ @myself = nil
17
+ end
18
+
19
+ def matches?(strings)
20
+ me = @name.to_s
21
+ strings.any? do |str|
22
+ re = /#{str}/
23
+ re === me or re === self.to_s
24
+ end
25
+ end
26
+
27
+ #
28
+ # Bounces messages up the context tree to the first enclosing context
29
+ # that knows how to deal with the request.
30
+ #
31
+ def method_missing(method, *args)
32
+ @outer.send(method, *args)
33
+ end
34
+
35
+ def num_methods
36
+ 0
37
+ end
38
+
39
+ def outer_name
40
+ "#{@name}/"
41
+ end
42
+
43
+ def to_s
44
+ "#{@outer.outer_name}#{@name}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,204 @@
1
+ require 'rubygems'
2
+ require 'parse_tree'
3
+ require 'sexp_processor'
4
+ require 'reek/block_context'
5
+ require 'reek/class_context'
6
+ require 'reek/module_context'
7
+ require 'reek/stop_context'
8
+ require 'reek/if_context'
9
+ require 'reek/method_context'
10
+ require 'reek/singleton_method_context'
11
+ require 'reek/yield_call_context'
12
+
13
+ module Reek
14
+
15
+ class CodeParser < SexpProcessor
16
+
17
+ def self.parse_tree_for(code) # :nodoc:
18
+ ParseTree.new.parse_tree_for_string(code)
19
+ end
20
+
21
+ # Creates a new Ruby code checker. Any smells discovered by
22
+ # +check_source+ or +check_object+ will be stored in +report+.
23
+ def initialize(report, smells, ctx = StopContext.new)
24
+ super()
25
+ @report = report
26
+ @smells = smells
27
+ @element = ctx
28
+ @unsupported -= [:cfunc]
29
+ @default_method = :process_default
30
+ @require_empty = @warn_on_default = false
31
+ end
32
+
33
+ # Analyses the given Ruby source +code+ looking for smells.
34
+ # Any smells found are saved in the +Report+ object that
35
+ # was passed to this object's constructor.
36
+ def check_source(code)
37
+ check_parse_tree(CodeParser.parse_tree_for(code))
38
+ end
39
+
40
+ # Analyses the given Ruby object +obj+ looking for smells.
41
+ # Any smells found are saved in the +Report+ object that
42
+ # was passed to this object's constructor.
43
+ def check_object(obj)
44
+ check_parse_tree(ParseTree.new.parse_tree(obj))
45
+ end
46
+
47
+ def process_default(exp)
48
+ exp[1..-1].each { |sub| process(sub) if Array === sub }
49
+ s(exp)
50
+ end
51
+
52
+ def process_module(exp)
53
+ push(ModuleContext.create(@element, exp)) do
54
+ process_default(exp)
55
+ check_smells(:module)
56
+ end
57
+ s(exp)
58
+ end
59
+
60
+ def process_class(exp)
61
+ push(ClassContext.create(@element, exp)) do
62
+ process_default(exp) unless @element.is_struct?
63
+ check_smells(:class)
64
+ end
65
+ s(exp)
66
+ end
67
+
68
+ def process_defn(exp)
69
+ handle_context(MethodContext, :defn, exp)
70
+ end
71
+
72
+ def process_defs(exp)
73
+ handle_context(SingletonMethodContext, :defs, exp)
74
+ end
75
+
76
+ def process_args(exp)
77
+ exp[1..-1].each {|sym| @element.record_parameter(sym) }
78
+ s(exp)
79
+ end
80
+
81
+ def process_attrset(exp)
82
+ @element.record_depends_on_self if /^@/ === exp[1].to_s
83
+ s(exp)
84
+ end
85
+
86
+ def process_lit(exp)
87
+ val = exp[1]
88
+ @element.record_depends_on_self if val == :self
89
+ s(exp)
90
+ end
91
+
92
+ def process_iter(exp)
93
+ process(exp[1])
94
+ handle_context(BlockContext, :iter, exp[1..-1])
95
+ end
96
+
97
+ def process_dasgn_curr(exp)
98
+ @element.record_parameter(exp[1])
99
+ process_default(exp)
100
+ end
101
+
102
+ def process_block(exp)
103
+ @element.count_statements(CodeParser.count_statements(exp))
104
+ process_default(exp)
105
+ end
106
+
107
+ def process_yield(exp)
108
+ handle_context(YieldCallContext, :yield, exp)
109
+ end
110
+
111
+ def process_call(exp)
112
+ @element.record_call_to(exp)
113
+ process_default(exp)
114
+ end
115
+
116
+ def process_fcall(exp)
117
+ @element.record_depends_on_self
118
+ @element.refs.record_reference_to_self
119
+ process_default(exp)
120
+ end
121
+
122
+ def process_cfunc(exp)
123
+ @element.record_depends_on_self
124
+ s(exp)
125
+ end
126
+
127
+ def process_vcall(exp)
128
+ @element.record_depends_on_self
129
+ @element.refs.record_reference_to_self
130
+ s(exp)
131
+ end
132
+
133
+ def process_if(exp)
134
+ handle_context(IfContext, :if, exp)
135
+ end
136
+
137
+ def process_ivar(exp)
138
+ process_iasgn(exp)
139
+ end
140
+
141
+ def process_lasgn(exp)
142
+ @element.record_local_variable(exp[1])
143
+ process(exp[2])
144
+ s(exp)
145
+ end
146
+
147
+ def process_iasgn(exp)
148
+ @element.record_instance_variable(exp[1])
149
+ @element.record_depends_on_self
150
+ process_default(exp)
151
+ end
152
+
153
+ def process_self(exp)
154
+ @element.record_depends_on_self
155
+ s(exp)
156
+ end
157
+
158
+ private
159
+
160
+ def self.count_statements(exp)
161
+ stmts = exp[1..-1]
162
+ ignore = 0
163
+ ignore = 1 if is_expr?(stmts[0], :args)
164
+ ignore += 1 if stmts[1] == s(:nil)
165
+ stmts.length - ignore
166
+ end
167
+
168
+ def self.is_expr?(exp, type)
169
+ Array === exp and exp[0] == type
170
+ end
171
+
172
+ def self.is_global_variable?(exp)
173
+ is_expr?(exp, :gvar)
174
+ end
175
+
176
+ def handle_context(klass, type, exp)
177
+ push(klass.new(@element, exp)) do
178
+ process_default(exp)
179
+ check_smells(type)
180
+ end
181
+ s(exp)
182
+ end
183
+
184
+ def check_smells(type)
185
+ @smells[type].each {|smell| smell.examine(@element, @report) }
186
+ end
187
+
188
+ def push(context)
189
+ orig = @element
190
+ @element = context
191
+ yield
192
+ @element = orig
193
+ end
194
+
195
+ def pop(exp)
196
+ @element = @element.outer
197
+ s(exp)
198
+ end
199
+
200
+ def check_parse_tree(sexp) # :nodoc:
201
+ sexp.each { |exp| process(exp) }
202
+ end
203
+ end
204
+ end