laser 0.7.0.pre1
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/.rspec +1 -0
- data/Gemfile +14 -0
- data/LICENSE +661 -0
- data/README.md +158 -0
- data/Rakefile +104 -0
- data/VERSION +1 -0
- data/bin/laser +7 -0
- data/design_docs/goals.md +57 -0
- data/design_docs/object_regex.md +426 -0
- data/design_docs/type_annotations.md +80 -0
- data/ext/laser/BasicBlock.cpp +572 -0
- data/ext/laser/BasicBlock.h +118 -0
- data/ext/laser/extconf.rb +3 -0
- data/features/laser.feature +25 -0
- data/features/step_definitions/laser_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/support/testdata/5_input +13 -0
- data/laser.gemspec +382 -0
- data/lib/laser.rb +98 -0
- data/lib/laser/analysis/annotations.rb +95 -0
- data/lib/laser/analysis/annotations/annotation_config.yaml +3 -0
- data/lib/laser/analysis/annotations/comment_attachment_annotation.rb +66 -0
- data/lib/laser/analysis/annotations/node_pointers_annotation.rb +36 -0
- data/lib/laser/analysis/annotations/runtime_annotation.rb +55 -0
- data/lib/laser/analysis/argument_expansion.rb +132 -0
- data/lib/laser/analysis/arity.rb +34 -0
- data/lib/laser/analysis/bindings.rb +144 -0
- data/lib/laser/analysis/bootstrap/bootstrap.rb +298 -0
- data/lib/laser/analysis/bootstrap/laser_class.rb +106 -0
- data/lib/laser/analysis/bootstrap/laser_method.rb +255 -0
- data/lib/laser/analysis/bootstrap/laser_module.rb +403 -0
- data/lib/laser/analysis/bootstrap/laser_module_copy.rb +74 -0
- data/lib/laser/analysis/bootstrap/laser_object.rb +69 -0
- data/lib/laser/analysis/bootstrap/laser_proc.rb +150 -0
- data/lib/laser/analysis/bootstrap/laser_singleton_class.rb +44 -0
- data/lib/laser/analysis/comments.rb +35 -0
- data/lib/laser/analysis/control_flow.rb +28 -0
- data/lib/laser/analysis/control_flow/alias_analysis.rb +31 -0
- data/lib/laser/analysis/control_flow/basic_block.rb +105 -0
- data/lib/laser/analysis/control_flow/cfg_builder.rb +2505 -0
- data/lib/laser/analysis/control_flow/cfg_instruction.rb +190 -0
- data/lib/laser/analysis/control_flow/constant_propagation.rb +742 -0
- data/lib/laser/analysis/control_flow/control_flow_graph.rb +370 -0
- data/lib/laser/analysis/control_flow/lifetime_analysis.rb +91 -0
- data/lib/laser/analysis/control_flow/method_call_search.rb +26 -0
- data/lib/laser/analysis/control_flow/raise_properties.rb +25 -0
- data/lib/laser/analysis/control_flow/simulation.rb +385 -0
- data/lib/laser/analysis/control_flow/static_single_assignment.rb +185 -0
- data/lib/laser/analysis/control_flow/unreachability_analysis.rb +57 -0
- data/lib/laser/analysis/control_flow/unused_variables.rb +91 -0
- data/lib/laser/analysis/control_flow/yield_properties.rb +103 -0
- data/lib/laser/analysis/errors.rb +131 -0
- data/lib/laser/analysis/laser_utils.rb +18 -0
- data/lib/laser/analysis/lexical_analysis.rb +172 -0
- data/lib/laser/analysis/method_call.rb +68 -0
- data/lib/laser/analysis/protocol_registry.rb +30 -0
- data/lib/laser/analysis/scope.rb +118 -0
- data/lib/laser/analysis/sexp.rb +159 -0
- data/lib/laser/analysis/sexp_analysis.rb +40 -0
- data/lib/laser/analysis/sexp_extensions/constant_extraction.rb +115 -0
- data/lib/laser/analysis/sexp_extensions/source_location.rb +164 -0
- data/lib/laser/analysis/sexp_extensions/type_inference.rb +47 -0
- data/lib/laser/analysis/signature.rb +76 -0
- data/lib/laser/analysis/special_methods/send.rb +67 -0
- data/lib/laser/analysis/unused_methods.rb +21 -0
- data/lib/laser/analysis/visitor.rb +141 -0
- data/lib/laser/annotation_parser/annotations.treetop +126 -0
- data/lib/laser/annotation_parser/annotations_parser.rb +748 -0
- data/lib/laser/annotation_parser/class_annotations.treetop +82 -0
- data/lib/laser/annotation_parser/class_annotations_parser.rb +654 -0
- data/lib/laser/annotation_parser/overload.treetop +24 -0
- data/lib/laser/annotation_parser/overload_parser.rb +167 -0
- data/lib/laser/annotation_parser/parsers.rb +6 -0
- data/lib/laser/annotation_parser/structural.treetop +37 -0
- data/lib/laser/annotation_parser/structural_parser.rb +406 -0
- data/lib/laser/annotation_parser/useful_parsers.treetop +47 -0
- data/lib/laser/annotation_parser/useful_parsers_parser.rb +674 -0
- data/lib/laser/rake/task.rb +46 -0
- data/lib/laser/runner.rb +189 -0
- data/lib/laser/scanner.rb +169 -0
- data/lib/laser/standard_library/_thread.rb +110 -0
- data/lib/laser/standard_library/abbrev.rb +103 -0
- data/lib/laser/standard_library/array.rb +418 -0
- data/lib/laser/standard_library/base64.rb +91 -0
- data/lib/laser/standard_library/basic_object.rb +55 -0
- data/lib/laser/standard_library/benchmark.rb +556 -0
- data/lib/laser/standard_library/bignum.rb +185 -0
- data/lib/laser/standard_library/cgi.rb +275 -0
- data/lib/laser/standard_library/cgi/cookie.rb +147 -0
- data/lib/laser/standard_library/cgi/core.rb +791 -0
- data/lib/laser/standard_library/cgi/html.rb +1021 -0
- data/lib/laser/standard_library/cgi/session.rb +537 -0
- data/lib/laser/standard_library/cgi/session/pstore.rb +111 -0
- data/lib/laser/standard_library/cgi/util.rb +188 -0
- data/lib/laser/standard_library/class_definitions.rb +333 -0
- data/lib/laser/standard_library/comparable.rb +125 -0
- data/lib/laser/standard_library/complex.rb +162 -0
- data/lib/laser/standard_library/enumerable.rb +178 -0
- data/lib/laser/standard_library/exceptions.rb +135 -0
- data/lib/laser/standard_library/fixnum.rb +188 -0
- data/lib/laser/standard_library/float.rb +180 -0
- data/lib/laser/standard_library/hash.rb +237 -0
- data/lib/laser/standard_library/integer.rb +123 -0
- data/lib/laser/standard_library/laser_magic.rb +7 -0
- data/lib/laser/standard_library/nil_false_true.rb +113 -0
- data/lib/laser/standard_library/numbers.rb +192 -0
- data/lib/laser/standard_library/proc.rb +31 -0
- data/lib/laser/standard_library/set.rb +1348 -0
- data/lib/laser/standard_library/string.rb +666 -0
- data/lib/laser/standard_library/stringio.rb +2 -0
- data/lib/laser/standard_library/symbol.rb +125 -0
- data/lib/laser/standard_library/tsort.rb +242 -0
- data/lib/laser/support/acts_as_struct.rb +66 -0
- data/lib/laser/support/frequency.rb +55 -0
- data/lib/laser/support/inheritable_attributes.rb +145 -0
- data/lib/laser/support/module_extensions.rb +94 -0
- data/lib/laser/support/placeholder_object.rb +13 -0
- data/lib/laser/third_party/rgl/adjacency.rb +221 -0
- data/lib/laser/third_party/rgl/base.rb +228 -0
- data/lib/laser/third_party/rgl/bidirectional.rb +39 -0
- data/lib/laser/third_party/rgl/condensation.rb +47 -0
- data/lib/laser/third_party/rgl/connected_components.rb +138 -0
- data/lib/laser/third_party/rgl/control_flow.rb +170 -0
- data/lib/laser/third_party/rgl/depth_first_spanning_tree.rb +37 -0
- data/lib/laser/third_party/rgl/dominators.rb +124 -0
- data/lib/laser/third_party/rgl/dot.rb +93 -0
- data/lib/laser/third_party/rgl/graphxml.rb +51 -0
- data/lib/laser/third_party/rgl/implicit.rb +174 -0
- data/lib/laser/third_party/rgl/mutable.rb +117 -0
- data/lib/laser/third_party/rgl/rdot.rb +445 -0
- data/lib/laser/third_party/rgl/topsort.rb +72 -0
- data/lib/laser/third_party/rgl/transitivity.rb +180 -0
- data/lib/laser/third_party/rgl/traversal.rb +348 -0
- data/lib/laser/types/types.rb +433 -0
- data/lib/laser/version.rb +14 -0
- data/lib/laser/warning.rb +149 -0
- data/lib/laser/warning_sets/default.yml +13 -0
- data/lib/laser/warnings/assignment_in_condition.rb +20 -0
- data/lib/laser/warnings/comment_spacing.rb +31 -0
- data/lib/laser/warnings/extra_blank_lines.rb +30 -0
- data/lib/laser/warnings/extra_whitespace.rb +16 -0
- data/lib/laser/warnings/hash_symbol_18_warning.rb +63 -0
- data/lib/laser/warnings/hash_symbol_19_warning.rb +29 -0
- data/lib/laser/warnings/line_length.rb +115 -0
- data/lib/laser/warnings/misaligned_unindentation.rb +17 -0
- data/lib/laser/warnings/operator_spacing.rb +68 -0
- data/lib/laser/warnings/parens_on_declaration.rb +30 -0
- data/lib/laser/warnings/rescue_exception.rb +42 -0
- data/lib/laser/warnings/semicolon.rb +25 -0
- data/lib/laser/warnings/sexp_errors.rb +24 -0
- data/lib/laser/warnings/uncalled_method_warning.rb +7 -0
- data/lib/laser/warnings/useless_double_quotes.rb +38 -0
- data/spec/analysis_specs/annotations_spec.rb +47 -0
- data/spec/analysis_specs/annotations_specs/comment_attachment_spec.rb +68 -0
- data/spec/analysis_specs/annotations_specs/node_pointers_annotation_spec.rb +90 -0
- data/spec/analysis_specs/annotations_specs/runtime_annotation_spec.rb +135 -0
- data/spec/analysis_specs/annotations_specs/spec_helper.rb +33 -0
- data/spec/analysis_specs/argument_expansion_spec.rb +113 -0
- data/spec/analysis_specs/bindings_spec.rb +36 -0
- data/spec/analysis_specs/comment_spec.rb +93 -0
- data/spec/analysis_specs/control_flow_specs/cfg_instruction_spec.rb +111 -0
- data/spec/analysis_specs/control_flow_specs/constant_propagation_spec.rb +560 -0
- data/spec/analysis_specs/control_flow_specs/control_flow_graph_spec.rb +5 -0
- data/spec/analysis_specs/control_flow_specs/raise_properties_spec.rb +310 -0
- data/spec/analysis_specs/control_flow_specs/raise_type_inference_spec.rb +301 -0
- data/spec/analysis_specs/control_flow_specs/return_type_inference_spec.rb +431 -0
- data/spec/analysis_specs/control_flow_specs/simulation_spec.rb +158 -0
- data/spec/analysis_specs/control_flow_specs/spec_helper.rb +110 -0
- data/spec/analysis_specs/control_flow_specs/tuple_misuse_inference_spec.rb +125 -0
- data/spec/analysis_specs/control_flow_specs/unreachability_analysis_spec.rb +76 -0
- data/spec/analysis_specs/control_flow_specs/unused_variable_spec.rb +99 -0
- data/spec/analysis_specs/control_flow_specs/yield_properties_spec.rb +372 -0
- data/spec/analysis_specs/error_spec.rb +30 -0
- data/spec/analysis_specs/laser_class_spec.rb +322 -0
- data/spec/analysis_specs/lexical_analysis_spec.rb +184 -0
- data/spec/analysis_specs/protocol_registry_spec.rb +63 -0
- data/spec/analysis_specs/scope_annotation_spec.rb +1013 -0
- data/spec/analysis_specs/scope_spec.rb +126 -0
- data/spec/analysis_specs/sexp_analysis_spec.rb +30 -0
- data/spec/analysis_specs/sexp_extension_specs/constant_extraction_spec.rb +309 -0
- data/spec/analysis_specs/sexp_extension_specs/source_location_spec.rb +231 -0
- data/spec/analysis_specs/sexp_extension_specs/spec_helper.rb +1 -0
- data/spec/analysis_specs/sexp_extension_specs/type_inference_spec.rb +252 -0
- data/spec/analysis_specs/sexp_spec.rb +167 -0
- data/spec/analysis_specs/spec_helper.rb +27 -0
- data/spec/analysis_specs/unused_methods_spec.rb +65 -0
- data/spec/analysis_specs/visitor_spec.rb +64 -0
- data/spec/annotation_parser_specs/annotations_parser_spec.rb +89 -0
- data/spec/annotation_parser_specs/class_annotation_parser_spec.rb +120 -0
- data/spec/annotation_parser_specs/overload_parser_spec.rb +39 -0
- data/spec/annotation_parser_specs/parsers_spec.rb +14 -0
- data/spec/annotation_parser_specs/spec_helper.rb +1 -0
- data/spec/annotation_parser_specs/structural_parser_spec.rb +67 -0
- data/spec/laser_spec.rb +14 -0
- data/spec/rake_specs/spec_helper.rb +1 -0
- data/spec/rake_specs/task_spec.rb +67 -0
- data/spec/runner_spec.rb +207 -0
- data/spec/scanner_spec.rb +75 -0
- data/spec/spec_helper.rb +121 -0
- data/spec/standard_library/exceptions_spec.rb +19 -0
- data/spec/standard_library/globals_spec.rb +14 -0
- data/spec/standard_library/set_spec.rb +31 -0
- data/spec/standard_library/spec_helper.rb +1 -0
- data/spec/standard_library/standard_library_spec.rb +302 -0
- data/spec/support_specs/acts_as_struct_spec.rb +94 -0
- data/spec/support_specs/frequency_spec.rb +23 -0
- data/spec/support_specs/module_extensions_spec.rb +117 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/type_specs/spec_helper.rb +1 -0
- data/spec/type_specs/types_spec.rb +133 -0
- data/spec/warning_spec.rb +95 -0
- data/spec/warning_specs/assignment_in_condition_spec.rb +68 -0
- data/spec/warning_specs/comment_spacing_spec.rb +65 -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/hash_symbol_18_warning_spec.rb +89 -0
- data/spec/warning_specs/hash_symbol_19_warning_spec.rb +63 -0
- data/spec/warning_specs/line_length_spec.rb +173 -0
- data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
- data/spec/warning_specs/operator_spacing_spec.rb +104 -0
- data/spec/warning_specs/parens_on_declaration_spec.rb +57 -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 +74 -0
- data/status_reports/2010/12/2010-12-14.md +163 -0
- data/status_reports/2010/12/2010-12-23.md +298 -0
- data/status_reports/2010/12/2010-12-24.md +6 -0
- data/test/third_party_tests/rgl_tests/TestComponents.rb +65 -0
- data/test/third_party_tests/rgl_tests/TestCycles.rb +61 -0
- data/test/third_party_tests/rgl_tests/TestDirectedGraph.rb +125 -0
- data/test/third_party_tests/rgl_tests/TestDot.rb +18 -0
- data/test/third_party_tests/rgl_tests/TestEdge.rb +34 -0
- data/test/third_party_tests/rgl_tests/TestGraph.rb +71 -0
- data/test/third_party_tests/rgl_tests/TestGraphXML.rb +57 -0
- data/test/third_party_tests/rgl_tests/TestImplicit.rb +52 -0
- data/test/third_party_tests/rgl_tests/TestRdot.rb +863 -0
- data/test/third_party_tests/rgl_tests/TestTransitivity.rb +129 -0
- data/test/third_party_tests/rgl_tests/TestTraversal.rb +220 -0
- data/test/third_party_tests/rgl_tests/TestUnDirectedGraph.rb +102 -0
- data/test/third_party_tests/rgl_tests/examples/north/Graph.log +128 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.0.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.1.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.11.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.12.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.13.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.14.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.15.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.16.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.17.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.19.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.2.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.20.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.22.graphml +43 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.24.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.25.graphml +45 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.27.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.28.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.29.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.3.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.30.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.31.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.34.graphml +42 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.37.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.38.graphml +38 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.39.graphml +36 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.4.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.40.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.41.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.42.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.45.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.46.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.5.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.50.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.56.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.57.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.58.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.6.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.60.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.61.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.62.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.68.graphml +30 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.69.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.7.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.70.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.71.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.72.graphml +28 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.74.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.75.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.78.graphml +27 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.79.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.8.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.80.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.82.graphml +35 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.83.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.85.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.86.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.88.graphml +37 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.89.graphml +29 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.9.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.90.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.91.graphml +31 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.92.graphml +26 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.93.graphml +32 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.10.94.graphml +34 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.12.8.graphml +40 -0
- data/test/third_party_tests/rgl_tests/examples/north/g.14.9.graphml +36 -0
- data/test/third_party_tests/rgl_tests/test_helper.rb +7 -0
- data/test/third_party_tests/test_inheritable_attributes.rb +187 -0
- metadata +470 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
module Laser::Analysis
|
|
2
|
+
module SexpExtensions
|
|
3
|
+
module SourceLocation
|
|
4
|
+
|
|
5
|
+
def line_number
|
|
6
|
+
source_begin && source_begin[0]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Calculates, with some lossiness, the start position of the current node
|
|
10
|
+
# in the original text. This will sometimes fail, as the AST does not include
|
|
11
|
+
# sufficient information in many cases to determine where a node lies. We
|
|
12
|
+
# have to figure it out based on nearby identifiers and keywords.
|
|
13
|
+
def source_begin
|
|
14
|
+
return @source_begin if @source_begin
|
|
15
|
+
default_result = children.select { |child| Sexp === child }.
|
|
16
|
+
map(&:source_begin).compact.first
|
|
17
|
+
@source_begin =
|
|
18
|
+
case type
|
|
19
|
+
when :@ident, :@int, :@kw, :@float, :@tstring_content, :@regexp_end,
|
|
20
|
+
:@ivar, :@cvar, :@gvar, :@const, :@label, :@CHAR, :@op
|
|
21
|
+
children[1]
|
|
22
|
+
when :regexp_literal
|
|
23
|
+
result = default_result.dup
|
|
24
|
+
if backtrack_expecting!(result, -1, '/') || backtrack_expecting!(result, -3, '%r')
|
|
25
|
+
result
|
|
26
|
+
end
|
|
27
|
+
when :string_literal
|
|
28
|
+
if default_result
|
|
29
|
+
result = default_result.dup # make a copy we can mutate
|
|
30
|
+
if backtrack_expecting!(result, -1, "'") ||
|
|
31
|
+
backtrack_expecting!(result, -1, '"') ||
|
|
32
|
+
backtrack_expecting!(result, -3, '%q') ||
|
|
33
|
+
backtrack_expecting!(result, -3, '%Q')
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
when :string_embexpr
|
|
38
|
+
if default_result
|
|
39
|
+
result = default_result.dup
|
|
40
|
+
result[1] -= 2
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
when :dyna_symbol
|
|
44
|
+
if default_result
|
|
45
|
+
result = default_result.dup
|
|
46
|
+
result[1] -= 2
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
when :symbol_literal
|
|
50
|
+
result = default_result.dup
|
|
51
|
+
result[1] -= 1
|
|
52
|
+
result
|
|
53
|
+
when :hash
|
|
54
|
+
backtrack_searching(default_result, '{') if default_result
|
|
55
|
+
when :array
|
|
56
|
+
backtrack_searching(default_result, '[') if default_result
|
|
57
|
+
when :def, :defs
|
|
58
|
+
backtrack_searching(default_result, 'def')
|
|
59
|
+
when :class, :sclass
|
|
60
|
+
backtrack_searching(default_result, 'class')
|
|
61
|
+
when :module
|
|
62
|
+
backtrack_searching(default_result, 'module')
|
|
63
|
+
else
|
|
64
|
+
default_result
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Calculates, with some lossiness, the end position of the current node
|
|
69
|
+
# in the original text. This will sometimes fail, as the AST does not include
|
|
70
|
+
# sufficient information in many cases to determine where a node ends. We
|
|
71
|
+
# have to figure it out based on nearby identifiers, keywords, and literals.
|
|
72
|
+
def source_end
|
|
73
|
+
default_result = children.select { |child| Sexp === child }.
|
|
74
|
+
map(&:source_end).compact.last
|
|
75
|
+
case type
|
|
76
|
+
when :@ident, :@int, :@kw, :@float, :@tstring_content, :@regexp_end,
|
|
77
|
+
:@ivar, :@cvar, :@gvar, :@const, :@label, :@CHAR, :@op
|
|
78
|
+
text, location = children
|
|
79
|
+
source_end = location.dup
|
|
80
|
+
source_end[1] += text.size
|
|
81
|
+
source_end
|
|
82
|
+
when :string_literal
|
|
83
|
+
if source_begin
|
|
84
|
+
result = default_result.dup
|
|
85
|
+
result[1] += 1
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
when :string_embexpr, :dyna_symbol
|
|
89
|
+
if default_result
|
|
90
|
+
result = default_result.dup
|
|
91
|
+
result[1] += 1
|
|
92
|
+
result
|
|
93
|
+
end
|
|
94
|
+
when :hash
|
|
95
|
+
forwardtrack_searching(default_result, '}') if default_result
|
|
96
|
+
when :array
|
|
97
|
+
forwardtrack_searching(default_result, ']') if default_result
|
|
98
|
+
else
|
|
99
|
+
default_result
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Searches for the given text starting at the given location, going backwards.
|
|
104
|
+
# Modifies the location to match the discovered expected text on success.
|
|
105
|
+
#
|
|
106
|
+
# complexity: O(N) wrt input source
|
|
107
|
+
# location: [Fixnum, Fixnum]
|
|
108
|
+
# expectation: String
|
|
109
|
+
# returns: Boolean
|
|
110
|
+
def backtrack_searching(location, expectation)
|
|
111
|
+
result = location.dup
|
|
112
|
+
line = lines[result[0] - 1]
|
|
113
|
+
begin
|
|
114
|
+
if (expectation_location = line.rindex(expectation, result[1]))
|
|
115
|
+
result[1] = expectation_location
|
|
116
|
+
return result
|
|
117
|
+
end
|
|
118
|
+
result[0] -= 1
|
|
119
|
+
line = lines[result[0] - 1]
|
|
120
|
+
result[1] = line.size
|
|
121
|
+
end while result[0] >= 0
|
|
122
|
+
location
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Searches for the given text starting at the given location, going backwards.
|
|
126
|
+
# Modifies the location to match the discovered expected text on success.
|
|
127
|
+
#
|
|
128
|
+
# complexity: O(N) wrt input source
|
|
129
|
+
# location: [Fixnum, Fixnum]
|
|
130
|
+
# expectation: String
|
|
131
|
+
# returns: Boolean
|
|
132
|
+
def forwardtrack_searching(location, expectation)
|
|
133
|
+
result = location.dup
|
|
134
|
+
line = lines[result[0] - 1]
|
|
135
|
+
begin
|
|
136
|
+
if (expectation_location = line.index(expectation, result[1]))
|
|
137
|
+
result[1] = expectation_location + expectation.size
|
|
138
|
+
return result
|
|
139
|
+
end
|
|
140
|
+
result[0] += 1
|
|
141
|
+
result[1] = 0
|
|
142
|
+
line = lines[result[0] - 1]
|
|
143
|
+
end while result[0] <= lines.size
|
|
144
|
+
location
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Attempts to backtrack for the given string from the given location.
|
|
148
|
+
# Returns true if successful.
|
|
149
|
+
def backtrack_expecting!(location, offset, expectation)
|
|
150
|
+
if text_at(location, offset, expectation.length) == expectation
|
|
151
|
+
location[1] += offset
|
|
152
|
+
true
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Determines the text at the given location tuple, with some offset,
|
|
157
|
+
# and a given length.
|
|
158
|
+
def text_at(location, offset, length)
|
|
159
|
+
line = lines[location[0] - 1]
|
|
160
|
+
line[location[1] + offset, length]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Laser::Analysis
|
|
2
|
+
module SexpExtensions
|
|
3
|
+
module TypeInference
|
|
4
|
+
# Finds the type of the AST node. This depends on the node's scope sometimes,
|
|
5
|
+
# and always upon its node type.
|
|
6
|
+
def expr_type
|
|
7
|
+
case self.type
|
|
8
|
+
when :string_literal, :@CHAR, :@tstring_content, :string_embexpr, :string_content,
|
|
9
|
+
:xstring_literal
|
|
10
|
+
Types::ClassObjectType.new('String')
|
|
11
|
+
when :@int
|
|
12
|
+
Types::ClassType.new('Integer', :covariant)
|
|
13
|
+
when :@float
|
|
14
|
+
Types::ClassObjectType.new('Float')
|
|
15
|
+
when :regexp_literal
|
|
16
|
+
@expr_type ||= Types::ClassObjectType.new('Regexp')
|
|
17
|
+
when :hash, :bare_assoc_hash
|
|
18
|
+
@expr_type ||= Types::ClassObjectType.new('Hash')
|
|
19
|
+
when :symbol_literal, :dyna_symbol, :@label
|
|
20
|
+
Types::ClassObjectType.new('Symbol')
|
|
21
|
+
when :array
|
|
22
|
+
Types::ClassObjectType.new('Array')
|
|
23
|
+
when :dot2, :dot3
|
|
24
|
+
Types::ClassObjectType.new('Range')
|
|
25
|
+
when :lambda
|
|
26
|
+
Types::ClassObjectType.new('Proc')
|
|
27
|
+
when :var_ref
|
|
28
|
+
ref = self[1]
|
|
29
|
+
if ref.type == :@kw && ref.expanded_identifier != 'self'
|
|
30
|
+
case ref[1]
|
|
31
|
+
when 'nil' then Types::ClassObjectType.new('NilClass')
|
|
32
|
+
when 'true' then Types::ClassObjectType.new('TrueClass')
|
|
33
|
+
when 'false' then Types::ClassObjectType.new('FalseClass')
|
|
34
|
+
when '__FILE__' then Types::ClassObjectType.new('String')
|
|
35
|
+
when '__LINE__' then Types::ClassObjectType.new('Fixnum')
|
|
36
|
+
when '__ENCODING__' then Types::ClassObjectType.new('Encoding')
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
self.scope.lookup(expanded_identifier).expr_type rescue Types::TOP
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
Laser::Types::TOP
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ArgumentListHandling
|
|
4
|
+
# Returns the hash representing the arguments in a signature, based on an
|
|
5
|
+
# argument list (:params) from the parser.
|
|
6
|
+
#
|
|
7
|
+
# @param Sexp arglist
|
|
8
|
+
# @return Symbol => Argument
|
|
9
|
+
def arg_list_for_arglist(arglist)
|
|
10
|
+
arglist = arglist.deep_find { |node| node.type == :params }
|
|
11
|
+
positional_1, optionals, rest_arg, positional_2, block_arg = arglist.children
|
|
12
|
+
arg_list = []
|
|
13
|
+
|
|
14
|
+
arg_list.concat parse_positionals(positional_1) if positional_1
|
|
15
|
+
arg_list.concat parse_optionals(optionals) if optionals
|
|
16
|
+
arg_list << parse_rest_arg(rest_arg) if rest_arg
|
|
17
|
+
arg_list.concat parse_positionals(positional_2) if positional_2
|
|
18
|
+
arg_list << parse_block_arg(block_arg) if block_arg
|
|
19
|
+
arg_list
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Adds the positional arguments to the argument hash/list.
|
|
23
|
+
#
|
|
24
|
+
# current_arg_hash: (Symbol => Argument)
|
|
25
|
+
# positional_list: Array<Sexp>
|
|
26
|
+
# return: Array<Bindings::ArgumentBinding>
|
|
27
|
+
def parse_positionals(positional_list)
|
|
28
|
+
positional_list.map do |node|
|
|
29
|
+
tag, name, lex = node
|
|
30
|
+
result = Bindings::ArgumentBinding.new(name, LaserObject.new(ClassRegistry['BasicObject']), :positional)
|
|
31
|
+
result.ast_node = node
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Parses a list of optional arguments in Sexp form and adds them
|
|
37
|
+
# to the argument hash.
|
|
38
|
+
#
|
|
39
|
+
# current_arg_hash: (Symbol => Argument)
|
|
40
|
+
# optionals: Array<Sexp>
|
|
41
|
+
def parse_optionals(optionals)
|
|
42
|
+
optionals.map do |node|
|
|
43
|
+
id, default_value = node
|
|
44
|
+
result = Bindings::ArgumentBinding.new(id.children.first, LaserObject.new(ClassRegistry['BasicObject']), :optional, default_value)
|
|
45
|
+
result.ast_node = node
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Parses the rest argument of an argument list Sexp and adds it to
|
|
51
|
+
# the argument hash.
|
|
52
|
+
#
|
|
53
|
+
# rest_arg: Sexp
|
|
54
|
+
def parse_rest_arg(rest_arg)
|
|
55
|
+
result = Bindings::ArgumentBinding.new(rest_arg[1][1], LaserObject.new(ClassRegistry['Array']), :rest)
|
|
56
|
+
result.ast_node = rest_arg
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Parses the block argument of an argument list Sexp and adds it to
|
|
61
|
+
# the argument hash.
|
|
62
|
+
#
|
|
63
|
+
# block_arg: Sexp
|
|
64
|
+
def parse_block_arg(block_arg)
|
|
65
|
+
result = Bindings::ArgumentBinding.new(block_arg[1][1], LaserObject.new(ClassRegistry['Proc']), :block)
|
|
66
|
+
result.ast_node = block_arg
|
|
67
|
+
result
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
module Signature
|
|
72
|
+
include Comparable
|
|
73
|
+
extend ArgumentListHandling
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module SpecialMethods
|
|
4
|
+
# Provides precise analysis of send methods. This is necessary for the
|
|
5
|
+
# analyzer to be able to tell where to look for semantic information when
|
|
6
|
+
# it encounters a call like this:
|
|
7
|
+
#
|
|
8
|
+
# method = unprovable_condition ? :foo : :bar
|
|
9
|
+
# send(method, 1, 2, 3)
|
|
10
|
+
#
|
|
11
|
+
# In this case, send() will return the union of whatever foo or bar return,
|
|
12
|
+
# and so on.
|
|
13
|
+
#
|
|
14
|
+
# This method supports both Kernel#send and Kernel#public_send.
|
|
15
|
+
class SendMethod < LaserMethod
|
|
16
|
+
def initialize(name, privacy)
|
|
17
|
+
super(name, nil)
|
|
18
|
+
@privacy = privacy
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def each_target_method(self_type, arg_type)
|
|
22
|
+
arg_type.possible_classes.each do |target_klass|
|
|
23
|
+
if LaserSingletonClass === target_klass
|
|
24
|
+
target_method_name = target_klass.get_instance.to_s
|
|
25
|
+
self_type.possible_classes.each do |self_class|
|
|
26
|
+
if passes_visibility?(self_class, target_method_name)
|
|
27
|
+
method = self_class.instance_method(target_method_name)
|
|
28
|
+
method.been_used!
|
|
29
|
+
yield(method)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def passes_visibility?(klass, name)
|
|
37
|
+
return true if @privacy == :any
|
|
38
|
+
klass.visibility_for(name) == @privacy
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def collect_type_from_targets(to_call, self_type, arg_types, block_type)
|
|
42
|
+
result_type = Types::UnionType.new([])
|
|
43
|
+
each_target_method(self_type, arg_types[0]) do |method|
|
|
44
|
+
result_type |= method.send(to_call, self_type, arg_types[1..-1], block_type)
|
|
45
|
+
end
|
|
46
|
+
result_type
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def return_type_for_types(self_type, arg_types, block_type)
|
|
50
|
+
collect_type_from_targets(:return_type_for_types, self_type, arg_types, block_type)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def raise_type_for_types(self_type, arg_types, block_type)
|
|
54
|
+
collect_type_from_targets(:raise_type_for_types, self_type, arg_types, block_type)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def raise_frequency_for_types(self_type, arg_types, block_type)
|
|
58
|
+
all_frequencies = []
|
|
59
|
+
each_target_method(self_type, arg_types[0]) do |method|
|
|
60
|
+
all_frequencies << method.raise_frequency_for_types(self_type, arg_types[1..-1], block_type)
|
|
61
|
+
end
|
|
62
|
+
Frequency.combine_samples(all_frequencies)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module UnusedMethodDetection
|
|
4
|
+
def self.unused_methods
|
|
5
|
+
methods = []
|
|
6
|
+
classes = Set[]
|
|
7
|
+
Analysis::ProtocolRegistry.class_protocols.each do |key, klass|
|
|
8
|
+
next if Analysis::LaserSingletonClass === klass || classes.include?(klass)
|
|
9
|
+
klass.__all_instance_methods(false).each do |name|
|
|
10
|
+
method = klass.instance_method(name)
|
|
11
|
+
unless method.dispatched? || method.builtin || method.special
|
|
12
|
+
methods << method
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
classes << klass
|
|
16
|
+
end
|
|
17
|
+
methods
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
# Visitor: a set of methods for visiting an AST. The
|
|
4
|
+
# default implementations visit each child and do no
|
|
5
|
+
# other processing. By including this module, and
|
|
6
|
+
# implementing certain methods, you can do your own
|
|
7
|
+
# processing on, say, every instance of a :rescue AST node.
|
|
8
|
+
# The default implementation will go arbitrarily deep in the AST
|
|
9
|
+
# tree until it hits a method you define.
|
|
10
|
+
module Visitor
|
|
11
|
+
extend ModuleExtensions
|
|
12
|
+
def self.included(klass)
|
|
13
|
+
klass.__send__(:extend, ClassMethods)
|
|
14
|
+
klass.__send__(:extend, ModuleExtensions)
|
|
15
|
+
class << klass
|
|
16
|
+
attr_writer :filters
|
|
17
|
+
def filters
|
|
18
|
+
@filters ||= []
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
module ClassMethods
|
|
23
|
+
extend ModuleExtensions
|
|
24
|
+
# A filter is a general-purpose functor run on a subset of the nodes in
|
|
25
|
+
# an AST. It includes a set of possible ways to match a node, including
|
|
26
|
+
# node types and general procs, and it includes a block to run in order
|
|
27
|
+
# to do some interesting manipulation of the matching node.
|
|
28
|
+
Filter = Struct.new(:args, :blk) do
|
|
29
|
+
def matches?(node)
|
|
30
|
+
args.any? do |filter|
|
|
31
|
+
case filter
|
|
32
|
+
when ::Symbol then node.type == filter
|
|
33
|
+
when Proc then filter.call(node, *node.children)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def inspect
|
|
39
|
+
args.inspect
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Runs the filter on the given node, with the visitor object as the
|
|
43
|
+
# 'self'. That way we create the illusion of methods using the #add
|
|
44
|
+
# syntax.
|
|
45
|
+
#
|
|
46
|
+
# If any analysis-specific errors are raised and not caught, they are
|
|
47
|
+
# simply attached to the node's errors list. If the filter would like
|
|
48
|
+
# to continue operating after an error is raised, it must catch it
|
|
49
|
+
# and attach the error itself.
|
|
50
|
+
def run(node, visitor)
|
|
51
|
+
begin
|
|
52
|
+
visitor.instance_exec(node, *node.children, &blk)
|
|
53
|
+
rescue Error => err
|
|
54
|
+
err.ast_node = node
|
|
55
|
+
node.errors << err
|
|
56
|
+
rescue StandardError => err
|
|
57
|
+
err.message.replace(err.message + " (Visitor #{self.inspect} Occurred at node #{node.inspect})")
|
|
58
|
+
raise err
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
# Adds a new filter with the given matching strategies and a block to run
|
|
63
|
+
# upon matching a node.
|
|
64
|
+
def add(*args, &blk)
|
|
65
|
+
(self.filters ||= []) << Filter.new(args, blk)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
attr_reader :text
|
|
70
|
+
# Annotates the given node +root+, assuming the tree represents the source contained in
|
|
71
|
+
# +text+. This is useful for text-based discovery that has to happen, often to capture
|
|
72
|
+
# lexing information lost by the Ripper parser.
|
|
73
|
+
#
|
|
74
|
+
# root: Sexp
|
|
75
|
+
# text: String
|
|
76
|
+
def annotate_with_text(root, text)
|
|
77
|
+
@text = text
|
|
78
|
+
@lines = nil
|
|
79
|
+
annotate! root
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Entry point for annotation. Should be called on the root of the tree we
|
|
83
|
+
# are interested in annotating.
|
|
84
|
+
#
|
|
85
|
+
# root: Sexp
|
|
86
|
+
def annotate!(root)
|
|
87
|
+
visit root
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Visits a given node. Will be automatically called by the visitor and can (and often
|
|
91
|
+
# should) be called manually.
|
|
92
|
+
#
|
|
93
|
+
# node: Sexp
|
|
94
|
+
def visit(node)
|
|
95
|
+
case node
|
|
96
|
+
when Sexp
|
|
97
|
+
case node[0]
|
|
98
|
+
when ::Symbol
|
|
99
|
+
try_filters node or default_visit node
|
|
100
|
+
when Array
|
|
101
|
+
default_visit(node)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Visits the children of the node, by calling #visit on every child of
|
|
107
|
+
# node that is a Sexp.
|
|
108
|
+
#
|
|
109
|
+
# node: Sexp
|
|
110
|
+
def visit_children(node)
|
|
111
|
+
node.children.select {|x| Sexp === x}.each {|x| visit(x) }
|
|
112
|
+
end
|
|
113
|
+
# By default, we should visit every child, trying to find something the visitor
|
|
114
|
+
# subclass has overridden.
|
|
115
|
+
alias_method :default_visit, :visit_children
|
|
116
|
+
|
|
117
|
+
# Tries all known filters on the given node, and if the filter matches, then
|
|
118
|
+
# the filter is run on the node. Returns whether or not any filters matched.
|
|
119
|
+
#
|
|
120
|
+
# node: Sexp
|
|
121
|
+
# return: Boolean
|
|
122
|
+
def try_filters(node)
|
|
123
|
+
any_ran = false
|
|
124
|
+
self.class.filters.each do |filter|
|
|
125
|
+
if filter.matches?(node)
|
|
126
|
+
filter.run(node, self)
|
|
127
|
+
any_ran = true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
any_ran
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
################## Source text manipulation methods ###############
|
|
134
|
+
|
|
135
|
+
def lines
|
|
136
|
+
@lines ||= text.lines.to_a
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|