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,57 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
module UnreachabilityAnalysis
|
|
5
|
+
# Dead Code Discovery: O(|V| + |E|)!
|
|
6
|
+
IGNORED_DEAD_CODE_NODES = [:@ident, :@op, :void_stmt]
|
|
7
|
+
def unreachable_vertices(dfst = depth_first_spanning_tree(self.enter))
|
|
8
|
+
vertices - dfst.vertices
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def perform_dead_code_discovery(delete_dead=false)
|
|
12
|
+
dfst = depth_first_spanning_tree(self.enter)
|
|
13
|
+
|
|
14
|
+
dead_verts = unreachable_vertices(dfst)
|
|
15
|
+
|
|
16
|
+
# then, go over all code in dead blocks, and mark potentially dead
|
|
17
|
+
# ast nodes.
|
|
18
|
+
# O(V)
|
|
19
|
+
dead_verts.each do |blk|
|
|
20
|
+
blk.instructions.each { |ins| ins.node.reachable = false if ins.node }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# run through all reachable statements and mark those nodes, and their
|
|
24
|
+
# parents, as partially executing.
|
|
25
|
+
#
|
|
26
|
+
# at most |V| nodes will have cur.reachable = true set.
|
|
27
|
+
# at most O(V) instructions will be visited total.
|
|
28
|
+
dfst.each_vertex do |blk|
|
|
29
|
+
blk.instructions.each do |ins|
|
|
30
|
+
cur = ins.node
|
|
31
|
+
while cur
|
|
32
|
+
cur.reachable = true
|
|
33
|
+
cur = cur.parent
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
dfs_for_dead_code self.root
|
|
38
|
+
dead_verts.each { |block| remove_vertex block } if delete_dead
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Performs a simple DFS, adding errors to any nodes that are still
|
|
42
|
+
# marked unreachable.
|
|
43
|
+
def dfs_for_dead_code(node)
|
|
44
|
+
if node.reachable
|
|
45
|
+
node.children.select { |x| Sexp === x }.reject do |child|
|
|
46
|
+
child == [] || child[0].is_a?(::Fixnum) || IGNORED_DEAD_CODE_NODES.include?(child.type)
|
|
47
|
+
end.each do |child|
|
|
48
|
+
dfs_for_dead_code(child)
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
node.add_error DeadCodeWarning.new('Dead code', node)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Finds unused variables in the control-flow graph. See thesis for
|
|
5
|
+
# the algorithm and proof of correctness.
|
|
6
|
+
module UnusedVariables
|
|
7
|
+
IGNORED_VARS = Set.new(%w(self t#current_exception t#current_block t#exit_exception t#exception_value $!))
|
|
8
|
+
|
|
9
|
+
# Adds unused variable warnings to all nodes which define a variable
|
|
10
|
+
# that is not used.
|
|
11
|
+
def add_unused_variable_warnings
|
|
12
|
+
unused_variables.reject { |var| var.name.start_with?('%') }.each do |temp|
|
|
13
|
+
# TODO(adgar): KILLMESOON
|
|
14
|
+
next unless temp.definition && temp.definition.node
|
|
15
|
+
next if IGNORED_VARS.include?(temp.non_ssa_name)
|
|
16
|
+
node = temp.definition.node
|
|
17
|
+
node.add_error(
|
|
18
|
+
UnusedVariableWarning.new("Variable defined but not used: #{temp.non_ssa_name}", node))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Finds the simply-unused variables: those which are defined and never
|
|
23
|
+
# read.
|
|
24
|
+
def simple_unused
|
|
25
|
+
variables = self.all_variables
|
|
26
|
+
result = variables.dup
|
|
27
|
+
variables.each do |var|
|
|
28
|
+
defn = var.definition
|
|
29
|
+
if defn.node.nil? || defn.node.reachable
|
|
30
|
+
var.uses.each do |use|
|
|
31
|
+
if use.node.nil? || use.node.reachable
|
|
32
|
+
result.delete var
|
|
33
|
+
else
|
|
34
|
+
var.uses.delete use
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
var.uses.clear
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Gets the set of unused variables. After SSA transformation, any
|
|
45
|
+
# variable with no uses need not be assigned. If the definition of
|
|
46
|
+
# that variable can be killed, then we remove that definition as a
|
|
47
|
+
# use of all its operands. This may result in further dead variables!
|
|
48
|
+
# N = number of vars
|
|
49
|
+
# O(N)
|
|
50
|
+
def unused_variables
|
|
51
|
+
worklist = Set.new(simple_unused())
|
|
52
|
+
Laser.debug_puts('>>> Finished finding simple unused vars <<<')
|
|
53
|
+
all_unused = Set.new
|
|
54
|
+
while worklist.any?
|
|
55
|
+
var = worklist.pop
|
|
56
|
+
if all_unused.add?(var)
|
|
57
|
+
definition = var.definition
|
|
58
|
+
if killable_with_unused_target?(definition)
|
|
59
|
+
definition.operands.each do |op|
|
|
60
|
+
next if op.name == 'self'
|
|
61
|
+
use_set = op.uses
|
|
62
|
+
use_set = use_set - [definition]
|
|
63
|
+
worklist << op if use_set.empty?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
all_unused
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def killable_with_unused_target?(insn)
|
|
72
|
+
case insn.type
|
|
73
|
+
when :assign, :phi, :lambda
|
|
74
|
+
true
|
|
75
|
+
when :call, :call_vararg
|
|
76
|
+
recv = insn[2]
|
|
77
|
+
if Bindings::ConstantBinding === insn[2]
|
|
78
|
+
true
|
|
79
|
+
elsif insn[2].value == UNDEFINED
|
|
80
|
+
true
|
|
81
|
+
else
|
|
82
|
+
insn[2].expr_type.matching_methods(insn[3].to_s).all?(&:pure)
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end # ControlFlow
|
|
90
|
+
end # Analysis
|
|
91
|
+
end #
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module ControlFlow
|
|
4
|
+
# Finds the properties of how the code yields to a block argument.
|
|
5
|
+
# Should not be used on top-level code, naturally.
|
|
6
|
+
module YieldProperties
|
|
7
|
+
def find_yield_properties(opts)
|
|
8
|
+
kernel_method_to_fix = ClassRegistry['Kernel'].
|
|
9
|
+
instance_method(:block_given?)
|
|
10
|
+
proc_method_to_fix = ClassRegistry['Proc'].singleton_class.
|
|
11
|
+
instance_method(:new)
|
|
12
|
+
magic_method_to_fix = ClassRegistry['Laser#Magic'].singleton_class.
|
|
13
|
+
instance_method(:current_block)
|
|
14
|
+
|
|
15
|
+
# Calculate the "no block provided" case
|
|
16
|
+
without_yield = dup
|
|
17
|
+
without_yield.bind_block_type(Types::NILCLASS)
|
|
18
|
+
without_yield.perform_constant_propagation(opts.merge(
|
|
19
|
+
fixed_methods: { kernel_method_to_fix => false,
|
|
20
|
+
proc_method_to_fix => nil }))
|
|
21
|
+
without_yield.kill_unexecuted_edges
|
|
22
|
+
|
|
23
|
+
weak_without_calls = without_yield.potential_block_calls(opts)
|
|
24
|
+
yield_pd = without_yield.yield_fail_postdominator
|
|
25
|
+
has_yield_pd = yield_pd && yield_pd.real_predecessors.size > 0
|
|
26
|
+
return_pd = without_yield.return_postdominator
|
|
27
|
+
has_return_pd = return_pd && return_pd.real_predecessors.size > 0
|
|
28
|
+
yields_without_block = has_yield_pd || weak_without_calls.size > 0
|
|
29
|
+
|
|
30
|
+
# Calculate the "has block provided" case
|
|
31
|
+
with_yield = dup
|
|
32
|
+
with_yield.bind_block_type(Types::PROC)
|
|
33
|
+
fake_block = proc { |*args| }
|
|
34
|
+
with_yield.perform_constant_propagation(opts.merge(
|
|
35
|
+
fixed_methods: { kernel_method_to_fix => true,
|
|
36
|
+
magic_method_to_fix => fake_block,
|
|
37
|
+
proc_method_to_fix => fake_block}))
|
|
38
|
+
with_yield.kill_unexecuted_edges
|
|
39
|
+
weak_with_calls = with_yield.potential_block_calls(fake_block, opts)
|
|
40
|
+
yields_with_block = weak_with_calls.size > 0
|
|
41
|
+
# if the mere difference in block presence results in no exit path, then
|
|
42
|
+
# we consider this evidence of failure due to lack of block.
|
|
43
|
+
yields_without_block = true if yields_with_block && !has_return_pd
|
|
44
|
+
|
|
45
|
+
@yield_type = compute_yield_type(yields_with_block, yields_without_block)
|
|
46
|
+
@yield_arity = calculate_yield_arity(weak_with_calls)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def compute_yield_type(with_block, without_block)
|
|
50
|
+
case [with_block, without_block]
|
|
51
|
+
when [true, true] then :required
|
|
52
|
+
when [true, false] then :optional
|
|
53
|
+
when [false, true] then :foolish
|
|
54
|
+
when [false, false] then :ignored
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def calculate_yield_arity(calls)
|
|
59
|
+
yield_arity = Set.new
|
|
60
|
+
calls.each do |call|
|
|
61
|
+
case call.type
|
|
62
|
+
when :call then yield_arity << (call.size - 5)
|
|
63
|
+
when :call_vararg then yield_arity << (0..Float::INFINITY)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
yield_arity
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def initial_block_aliases(opts)
|
|
70
|
+
proc_new_calls = find_method_calls(
|
|
71
|
+
ClassRegistry['Proc'].singleton_class.instance_method(:new), opts)
|
|
72
|
+
registers = proc_new_calls.map { |insn| insn[1] }
|
|
73
|
+
initial_aliases = Set.new(registers)
|
|
74
|
+
initial_aliases.add(block_register)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def potential_block_calls(block_value = nil, opts)
|
|
78
|
+
aliases = weak_local_aliases_for(initial_block_aliases(opts), block_value)
|
|
79
|
+
calls = []
|
|
80
|
+
reachable_vertices do |block|
|
|
81
|
+
block.instructions.each do |insn|
|
|
82
|
+
# consider runtime aliases, don't hardcode known ones
|
|
83
|
+
if (insn.type == :call || insn.type == :call_vararg) &&
|
|
84
|
+
(insn[3] == :call || insn[3] == :=== || insn[3] == :[]) && aliases.include?(insn[2])
|
|
85
|
+
calls << insn
|
|
86
|
+
elsif aliases.include?(insn.block_operand)
|
|
87
|
+
insn.possible_methods(opts).each do |method|
|
|
88
|
+
yield_type = method.yield_type
|
|
89
|
+
if block_value.nil? && (yield_type == :required || yield_type == :foolish) ||
|
|
90
|
+
!block_value.nil? && (yield_type == :required || yield_type == :optional)
|
|
91
|
+
calls << insn
|
|
92
|
+
break
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
calls
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end # ControlFlow
|
|
102
|
+
end # Analysis
|
|
103
|
+
end #
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
# Base class for all Laser errors. Has a few fields that are important for later
|
|
3
|
+
# referencing during error reporting.
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
ADVISORY = 1
|
|
6
|
+
STYLE = 2
|
|
7
|
+
MINOR_WARNING = 3
|
|
8
|
+
WARNING = 4
|
|
9
|
+
MAJOR_WARNING = 5
|
|
10
|
+
SIMPLE_ERROR = 6
|
|
11
|
+
ERROR = 7
|
|
12
|
+
TRICKY_ERROR = 8
|
|
13
|
+
MAJOR_ERROR = 9
|
|
14
|
+
FUCKUP = 10
|
|
15
|
+
|
|
16
|
+
def self.severity(new_severity)
|
|
17
|
+
define_method :initialize do |msg, node|
|
|
18
|
+
super(msg, node, new_severity)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_accessor :ast_node, :severity
|
|
23
|
+
def initialize(message, ast_node, severity = 5)
|
|
24
|
+
super(message)
|
|
25
|
+
@ast_node, @severity = ast_node, severity
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def ==(other)
|
|
29
|
+
other.class == self.class && other.message == self.message &&
|
|
30
|
+
other.ast_node == self.ast_node && self.severity == other.severity
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def line_number
|
|
34
|
+
ast_node.line_number
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def inspect
|
|
38
|
+
"#<#{self.class}: #{message} (#{ast_node.file_name}:#{ast_node.source_begin[0]})>"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class ReopenedClassAsModuleError < Laser::Error
|
|
43
|
+
severity MAJOR_ERROR
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class ReopenedModuleAsClassError < Laser::Error
|
|
47
|
+
severity MAJOR_ERROR
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class ConstantInForLoopError < Laser::Error
|
|
51
|
+
def initialize(const_name, ast)
|
|
52
|
+
super("The constant #{const_name} is a loop variable in a for loop.",
|
|
53
|
+
ast, MAJOR_ERROR)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class DoubleIncludeError < Laser::Error
|
|
58
|
+
severity MAJOR_WARNING
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class DynamicSuperclassError < Laser::Error
|
|
62
|
+
severity MAJOR_ERROR
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class NoSuchMethodError < Laser::Error
|
|
66
|
+
severity MAJOR_ERROR
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class NotInMethodError < Laser::Error
|
|
70
|
+
severity FUCKUP
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class IncompatibleArityError < Laser::Error
|
|
74
|
+
severity MAJOR_ERROR
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class FailedAliasError < Laser::Error
|
|
78
|
+
severity MAJOR_ERROR
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class SuperclassMismatchError < Laser::Error
|
|
82
|
+
severity TRICKY_ERROR
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class DeadCodeWarning < Laser::Error
|
|
86
|
+
severity MAJOR_WARNING
|
|
87
|
+
def initialize(message, ast_node)
|
|
88
|
+
if ast_node.source_begin
|
|
89
|
+
super("Dead Code #{ast_node.source_begin[0]}:#{ast_node.source_begin[1]}", ast_node)
|
|
90
|
+
else
|
|
91
|
+
super("Dead Code", ast_node)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class UnusedVariableWarning < Laser::Error
|
|
97
|
+
severity WARNING
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class TopLevelSimulationRaised < Laser::Error
|
|
101
|
+
attr_reader :error
|
|
102
|
+
def initialize(message, ast_node, detected_error)
|
|
103
|
+
super(message, ast_node, FUCKUP)
|
|
104
|
+
@error = detected_error
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class NoMatchingTypeSignature < Laser::Error
|
|
109
|
+
severity MAJOR_ERROR
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class MissingBlockError < Laser::Error
|
|
113
|
+
severity TRICKY_ERROR
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class UnnecessaryBlockError < Laser::Error
|
|
117
|
+
severity TRICKY_ERROR
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class ImproperOverloadTypeError < Laser::Error
|
|
121
|
+
severity TRICKY_ERROR
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class DiscardedRHSError < Laser::Error
|
|
125
|
+
severity SIMPLE_ERROR
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class UnassignedLHSError < Laser::Error
|
|
129
|
+
severity SIMPLE_ERROR
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
module Analysis
|
|
3
|
+
module Utilities
|
|
4
|
+
module_function
|
|
5
|
+
def klass_for(arg)
|
|
6
|
+
LaserObject === arg ? arg.singleton_class : SingletonClassFactory.create_for(arg)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def type_for(arg, variance=:invariant)
|
|
10
|
+
Types::ClassObjectType.new(klass_for(arg))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def normal_class_for(arg)
|
|
14
|
+
LaserObject === arg ? arg.normal_class : ClassRegistry[arg.class.name]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
module Laser
|
|
2
|
+
# This is a set of methods that get provided to Warnings so they can perform
|
|
3
|
+
# lexical analysis of their bodies. This module handles tokenizing only - not
|
|
4
|
+
# parse-trees.
|
|
5
|
+
module LexicalAnalysis
|
|
6
|
+
# This is a wrapper class around the tokens returned by Ripper. Since the
|
|
7
|
+
# tokens are just arrays, this class lets us use nice mnemonics with almost zero
|
|
8
|
+
# runtime overhead.
|
|
9
|
+
Token = Struct.new(:type, :body, :line, :col) do
|
|
10
|
+
# Unpacks the token from Ripper and breaks it into its separate components.
|
|
11
|
+
#
|
|
12
|
+
# @param [Array<Array<Integer, Integer>, Symbol, String>] token the token
|
|
13
|
+
# from Ripper that we're wrapping
|
|
14
|
+
def initialize(token)
|
|
15
|
+
(self.line, self.col), self.type, self.body = token
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def reg_desc
|
|
19
|
+
type.to_s.gsub(/^on_/, '')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Lexes the given text.
|
|
24
|
+
#
|
|
25
|
+
# @param [String] body (self.body) The text to lex
|
|
26
|
+
# @return [Array<Array<Integer, Integer>, Symbol, String>] A set of tokens
|
|
27
|
+
# in Ripper's result format. Each token is an array of the form:
|
|
28
|
+
# [[1, token_position], token_type, token_text]. I'm not exactly clear on
|
|
29
|
+
# why the 1 is always there. At any rate - the result is an array of those
|
|
30
|
+
# tokens.
|
|
31
|
+
def lex(body = self.body, token_class = Token)
|
|
32
|
+
return [] if body =~ /^#.*encoding.*/
|
|
33
|
+
Ripper.lex(body).map {|token| token_class.new(token) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the text between two token positions. The token positions are
|
|
37
|
+
# in [line, column] format. The body, left, and right tokens must be provided,
|
|
38
|
+
# and optionally, you can override the inclusiveness of the text-between operation.
|
|
39
|
+
# It defaults to :none, for including neither the left nor right tokens in the
|
|
40
|
+
# result. You can pass :none, :left, :right, or :both.
|
|
41
|
+
#
|
|
42
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
43
|
+
# to search. This defaults to the full text.
|
|
44
|
+
# @param [Token] left the left token to get the text between
|
|
45
|
+
# @param [Token] right the right token to get the text between
|
|
46
|
+
# @param [Symbol] inclusive should the :left, :right, :both, or :none tokens
|
|
47
|
+
# be included in the resulting text?
|
|
48
|
+
# @return the text between the two tokens within the text. This is necessary
|
|
49
|
+
# because the lexer provides [line, column] coordinates which is quite
|
|
50
|
+
# unfortunate.
|
|
51
|
+
def text_between_token_positions(text, left, right, inclusive = :none)
|
|
52
|
+
result = ""
|
|
53
|
+
lines = text.lines.to_a
|
|
54
|
+
left.line.upto(right.line) do |cur_line|
|
|
55
|
+
line = lines[cur_line - 1]
|
|
56
|
+
result << left.body if cur_line == left.line && (inclusive == :both || inclusive == :left)
|
|
57
|
+
left_bound = cur_line == left.line ? left.col + left.body.size : 0
|
|
58
|
+
right_bound = cur_line == right.line ? right.col - 1 : -1
|
|
59
|
+
result << line[left_bound..right_bound]
|
|
60
|
+
result << right.body if cur_line == right.line && (inclusive == :both || inclusive == :right)
|
|
61
|
+
end
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Searches for the given token using standard [body], target symbols syntax.
|
|
66
|
+
# Yields for each token found that matches the query, and returns all those
|
|
67
|
+
# who match.
|
|
68
|
+
#
|
|
69
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
70
|
+
# to search. This defaults to the full text.
|
|
71
|
+
# @param [Symbol] token The rest of the arguments are tokens to search
|
|
72
|
+
# for. Any number of tokens may be specified.
|
|
73
|
+
# @return [Array<Array>] All the matching tokens for the query
|
|
74
|
+
# @deprecated
|
|
75
|
+
#
|
|
76
|
+
# def select_token(*args)
|
|
77
|
+
# body, list = _extract_token_search_args(args)
|
|
78
|
+
# result = []
|
|
79
|
+
# while (token = find_token(body, *list)) && token != nil
|
|
80
|
+
# result << token if yield(*token)
|
|
81
|
+
# _, body = split_on_token(body, *list)
|
|
82
|
+
# body = body[token.body.size..-1]
|
|
83
|
+
# end
|
|
84
|
+
# return result
|
|
85
|
+
# end
|
|
86
|
+
|
|
87
|
+
# Finds the first instance of a set of keywords in the body. If no text is
|
|
88
|
+
# given to scan, then the full content is scanned.
|
|
89
|
+
#
|
|
90
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
91
|
+
# to search. This defaults to the full text.
|
|
92
|
+
# @param [Symbol] keyword The rest of the arguments are keywords to search
|
|
93
|
+
# for. Any number of keywords may be specified.
|
|
94
|
+
# @return [Array] the token in the form returned by Ripper. See #lex.
|
|
95
|
+
def find_keyword(*args)
|
|
96
|
+
body, list = _extract_token_search_args(args)
|
|
97
|
+
list.map! {|x| x.to_s}
|
|
98
|
+
lexed = lex(body)
|
|
99
|
+
lexed.find.with_index do |tok, idx|
|
|
100
|
+
is_keyword = tok.type == :on_kw && list.include?(tok.body)
|
|
101
|
+
is_not_symbol = idx == 0 || lexed[idx-1].type != :on_symbeg
|
|
102
|
+
is_keyword && is_not_symbol
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Finds the first instance of a set of tokens in the body. If no text is
|
|
107
|
+
# given to scan, then the full content is scanned.
|
|
108
|
+
#
|
|
109
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
110
|
+
# to search. This defaults to the full text.
|
|
111
|
+
# @param [Symbol] token The rest of the arguments are tokens to search
|
|
112
|
+
# for. Any number of tokens may be specified.
|
|
113
|
+
# @return [Array] the token in the form returned by Ripper. See #lex.
|
|
114
|
+
def find_token(*args)
|
|
115
|
+
body, list = _extract_token_search_args(args)
|
|
116
|
+
# grr match comment with encoding in it
|
|
117
|
+
lexed = lex(body)
|
|
118
|
+
lexed.find.with_index do |tok, idx|
|
|
119
|
+
is_token = list.include?(tok.type)
|
|
120
|
+
is_not_symbol = idx == 0 || lexed[idx-1].type != :on_symbeg
|
|
121
|
+
is_token && is_not_symbol
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Splits the body into two halfs based on the first appearance of a keyword.
|
|
126
|
+
#
|
|
127
|
+
# @example
|
|
128
|
+
# split_on_keyword('x = 5 unless y == 2', :unless)
|
|
129
|
+
# # => ['x = 5 ', 'unless y == 2']
|
|
130
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
131
|
+
# to search. This defaults to the full text.
|
|
132
|
+
# @param [Symbol] token The rest of the arguments are keywords to search
|
|
133
|
+
# for. Any number of keywords may be specified.
|
|
134
|
+
# @return [Array<String, String>] The body split by the keyword.
|
|
135
|
+
def split_on_keyword(*args)
|
|
136
|
+
body, keywords = _extract_token_search_args(args)
|
|
137
|
+
token = find_keyword(body, *keywords)
|
|
138
|
+
return _split_body_with_raw_token(body, token)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Splits the body into two halfs based on the first appearance of a token.
|
|
142
|
+
#
|
|
143
|
+
# @example
|
|
144
|
+
# split_on_token('x = 5 unless y == 2', :on_kw)
|
|
145
|
+
# # => ['x = 5 ', 'unless y == 2']
|
|
146
|
+
# @param [String] body (self.body) The first parameter is optional: the text
|
|
147
|
+
# to search. This defaults to the full text.
|
|
148
|
+
# @param [Symbol] token The rest of the arguments are tokens to search
|
|
149
|
+
# for. Any number of tokens may be specified.
|
|
150
|
+
# @return [Array<String, String>] The body split by the token.
|
|
151
|
+
def split_on_token(*args)
|
|
152
|
+
body, tokens = _extract_token_search_args(args)
|
|
153
|
+
token = find_token(body, *tokens)
|
|
154
|
+
return _split_body_with_raw_token(body, token)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
private
|
|
158
|
+
|
|
159
|
+
def _extract_token_search_args(args)
|
|
160
|
+
if args.first.is_a?(String)
|
|
161
|
+
return args[0], args[1..-1]
|
|
162
|
+
else
|
|
163
|
+
return self.body, args
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def _split_body_with_raw_token(body, token)
|
|
168
|
+
max = token ? [0, token.col].max : body.size
|
|
169
|
+
return body[0,max], body[max..-1]
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|