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,110 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
def cfg_builder_for(input)
|
|
4
|
+
ControlFlow::GraphBuilder.new(annotate_all(input)[1][0][3])
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
RSpec::Matchers.define :have_error do |klass|
|
|
8
|
+
chain :on_line do |number|
|
|
9
|
+
@line = number
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
chain :with_message do |message|
|
|
13
|
+
@message_matcher = message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
match do |graph|
|
|
17
|
+
graph.all_errors.any? do |err|
|
|
18
|
+
@matches_class = err.is_a?(klass)
|
|
19
|
+
@matches_line = !@line || err.line_number == @line
|
|
20
|
+
@matches_message = if String === @message_matcher
|
|
21
|
+
err.message == @message_matcher
|
|
22
|
+
elsif Regexp === @message_matcher
|
|
23
|
+
err.message =~ @message_matcher
|
|
24
|
+
else
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
@matches_message && @matches_line && @matches_class
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
failure_message_for_should do |graph|
|
|
32
|
+
result = 'expected an error that'
|
|
33
|
+
result << " was of the class #{klass.name}" if !@matches_message
|
|
34
|
+
result << " was on line #@line" if !@matches_line
|
|
35
|
+
if !@matches_message
|
|
36
|
+
result << " matches the regex #{@message_matcher}" if Regexp === @message_matcher
|
|
37
|
+
result << " matches the regex #{@message_matcher}" if String === @message_matcher
|
|
38
|
+
end
|
|
39
|
+
result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
failure_message_for_should_not do |graph|
|
|
43
|
+
"Expected to not find any errors of class #{klass.name}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
RSpec::Matchers.define :have_constant do |name|
|
|
48
|
+
chain :with_value do |value|
|
|
49
|
+
@value = value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
match do |graph|
|
|
53
|
+
graph.constants.keys.find do |var|
|
|
54
|
+
next unless var.non_ssa_name == name
|
|
55
|
+
@constant = var
|
|
56
|
+
@value ||= nil
|
|
57
|
+
if @value
|
|
58
|
+
@constant && (@constant.value == @value)
|
|
59
|
+
else
|
|
60
|
+
@constant
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
failure_message_for_should do |graph|
|
|
66
|
+
if !@constant
|
|
67
|
+
"Expected variable '#{name}' to be inferred as a constant, but it was not."
|
|
68
|
+
elsif @constant.value != @value
|
|
69
|
+
"Expected variable '#{name}' to have value #{@value}, but it was #{@constant.value}."
|
|
70
|
+
else
|
|
71
|
+
"UNEXPECTED FAILURE?!"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
failure_message_for_should_not do |graph|
|
|
76
|
+
if @constant && @constant.value == @value
|
|
77
|
+
"Expected variable '#{name}' to not have value #{@value}, but it was #{@constant.value}."
|
|
78
|
+
elsif @constant
|
|
79
|
+
"Expected variable '#{name}' to not be inferred as a constant, but it was."
|
|
80
|
+
else
|
|
81
|
+
"UNEXPECTED FAILURE?!"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
include Laser::Analysis
|
|
88
|
+
def annotate_all_cfg(body)
|
|
89
|
+
inputs = [['(stdin)', body]]
|
|
90
|
+
inputs.map! do |filename, text|
|
|
91
|
+
[filename, text, Sexp.new(RipperPlus.sexp(text), filename, text)]
|
|
92
|
+
end
|
|
93
|
+
Annotations.apply_inherited_attributes(inputs)
|
|
94
|
+
inputs[0][2]
|
|
95
|
+
end
|
|
96
|
+
def cfg(input)
|
|
97
|
+
cfg_builder = ControlFlow::GraphBuilder.new(annotate_all_cfg(input))
|
|
98
|
+
graph = cfg_builder.build
|
|
99
|
+
graph.analyze
|
|
100
|
+
graph
|
|
101
|
+
end
|
|
102
|
+
def cfg_method(input)
|
|
103
|
+
method_tree = annotate_all_cfg(input)
|
|
104
|
+
body = method_tree.find_type(:bodystmt)
|
|
105
|
+
cfg_builder = ControlFlow::GraphBuilder.new(
|
|
106
|
+
body, Signature.arg_list_for_arglist(body.prev))
|
|
107
|
+
graph = cfg_builder.build
|
|
108
|
+
graph.analyze
|
|
109
|
+
graph
|
|
110
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Tuple misuse inference' do
|
|
4
|
+
it 'should find code that has statically too few mlhs for mrhs' do
|
|
5
|
+
g = cfg_method <<-EOF
|
|
6
|
+
def foo(x)
|
|
7
|
+
a, b, c, d = x, x
|
|
8
|
+
end
|
|
9
|
+
EOF
|
|
10
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(2).with_message(/\(c\)/)
|
|
11
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(2).with_message(/\(d\)/)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should find code that has statically too many mlhs for mrhs' do
|
|
15
|
+
g = cfg_method <<-EOF
|
|
16
|
+
def foo(x)
|
|
17
|
+
a, d = x, x, x
|
|
18
|
+
end
|
|
19
|
+
EOF
|
|
20
|
+
g.should have_error(Laser::DiscardedRHSError).on_line(2)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'should find statically useless LHS splats' do
|
|
24
|
+
g = cfg_method <<-EOF
|
|
25
|
+
def foo(x)
|
|
26
|
+
a, *b, d = x, x
|
|
27
|
+
end
|
|
28
|
+
EOF
|
|
29
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(2).with_message(/\(b\)/)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should find statically useless LHS unnamed splats and report them differently' do
|
|
33
|
+
g = cfg_method <<-EOF
|
|
34
|
+
def foo(x)
|
|
35
|
+
a, *, d = x, x
|
|
36
|
+
end
|
|
37
|
+
EOF
|
|
38
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(2).with_message(/Unnamed LHS/i)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should find statically discarded RHS splats' do
|
|
42
|
+
g = cfg_method <<-EOF
|
|
43
|
+
def foo(x)
|
|
44
|
+
a, b, c = x, x, x, *x
|
|
45
|
+
end
|
|
46
|
+
EOF
|
|
47
|
+
g.should have_error(Laser::DiscardedRHSError).on_line(2).with_message(/splat/i)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'should find dynamically-proven unassigned LHS vars' do
|
|
51
|
+
g = cfg <<-EOF
|
|
52
|
+
class TupleMisuse1
|
|
53
|
+
def foo(x)
|
|
54
|
+
a, b, c = bar(x)
|
|
55
|
+
end
|
|
56
|
+
def bar(x)
|
|
57
|
+
[x, x]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
TupleMisuse1.new.foo(gets)
|
|
61
|
+
EOF
|
|
62
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(3)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should find dynamically-proven discarded RHS vals' do
|
|
66
|
+
g = cfg <<-EOF
|
|
67
|
+
class TupleMisuse1
|
|
68
|
+
def foo(x)
|
|
69
|
+
a, b = bar(x)
|
|
70
|
+
end
|
|
71
|
+
def bar(x)
|
|
72
|
+
[x, x, x]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
TupleMisuse1.new.foo(gets)
|
|
76
|
+
EOF
|
|
77
|
+
g.should have_error(Laser::DiscardedRHSError).on_line(3)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'should find dynamically-proven unassigned LHS splats' do
|
|
81
|
+
g = cfg <<-EOF
|
|
82
|
+
class TupleMisuse1
|
|
83
|
+
def foo(x)
|
|
84
|
+
a, *b, c = bar(x)
|
|
85
|
+
end
|
|
86
|
+
def bar(x)
|
|
87
|
+
[x, x]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
TupleMisuse1.new.foo(gets)
|
|
91
|
+
EOF
|
|
92
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(3)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'should find dynamically-proven wasted RHS splat vals' do
|
|
96
|
+
g = cfg <<-EOF
|
|
97
|
+
class TupleMisuse1
|
|
98
|
+
def foo(x)
|
|
99
|
+
a, b, c = x, x, *bar(x)
|
|
100
|
+
end
|
|
101
|
+
def bar(x)
|
|
102
|
+
[x, x]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
TupleMisuse1.new.foo(gets)
|
|
106
|
+
EOF
|
|
107
|
+
g.should have_error(Laser::DiscardedRHSError).on_line(3)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
it 'should find dynamically-proven unassigned LHS splats with an RHS splat too' do
|
|
112
|
+
g = cfg <<-EOF
|
|
113
|
+
class TupleMisuse1
|
|
114
|
+
def foo(x)
|
|
115
|
+
a, *b, c, d = x, *bar(x)
|
|
116
|
+
end
|
|
117
|
+
def bar(x)
|
|
118
|
+
[x, x]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
TupleMisuse1.new.foo(gets)
|
|
122
|
+
EOF
|
|
123
|
+
g.should have_error(Laser::UnassignedLHSError).on_line(3)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ControlFlow::UnreachabilityAnalysis do
|
|
4
|
+
it 'should find code that follows explicit return' do
|
|
5
|
+
g = cfg_method <<-EOF
|
|
6
|
+
def foo(x)
|
|
7
|
+
y = gets() * 2
|
|
8
|
+
return y
|
|
9
|
+
p y
|
|
10
|
+
end
|
|
11
|
+
EOF
|
|
12
|
+
g.should have_error(Laser::DeadCodeWarning).on_line(4)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
%w{next break redo}.each do |keyword|
|
|
16
|
+
it "should find code that follows a #{keyword}" do
|
|
17
|
+
g = cfg_method <<-EOF
|
|
18
|
+
def foo(x)
|
|
19
|
+
while y = gets() * 2
|
|
20
|
+
p y
|
|
21
|
+
#{keyword}
|
|
22
|
+
z = y.foo
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
EOF
|
|
26
|
+
g.should have_error(Laser::DeadCodeWarning).on_line(5)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should find code that follows an if/else/elsif in which each branch jumps' do
|
|
31
|
+
g = cfg_method <<-EOF
|
|
32
|
+
def foo(x)
|
|
33
|
+
y = gets() * 2
|
|
34
|
+
if y.size > 10
|
|
35
|
+
return y
|
|
36
|
+
elsif y.size < 10
|
|
37
|
+
return y[0..4]
|
|
38
|
+
else
|
|
39
|
+
return 'hello'
|
|
40
|
+
end
|
|
41
|
+
puts y
|
|
42
|
+
end
|
|
43
|
+
EOF
|
|
44
|
+
g.should have_error(Laser::DeadCodeWarning).on_line(10)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'should find code that never runs due to constant propagation' do
|
|
48
|
+
g = cfg_method <<-EOF
|
|
49
|
+
def foo(x)
|
|
50
|
+
y = 'hello' * 3
|
|
51
|
+
if y == 'hellohellohello'
|
|
52
|
+
puts gets
|
|
53
|
+
z = 3
|
|
54
|
+
else
|
|
55
|
+
z = 10
|
|
56
|
+
end
|
|
57
|
+
a = z
|
|
58
|
+
end
|
|
59
|
+
EOF
|
|
60
|
+
g.should have_error(Laser::DeadCodeWarning).on_line(7)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should find code that never runs due to CP + local type inference' do
|
|
64
|
+
g = cfg_method <<-EOF
|
|
65
|
+
def foo
|
|
66
|
+
a = gets
|
|
67
|
+
if a.strip # no !, always returns a String
|
|
68
|
+
10
|
|
69
|
+
else
|
|
70
|
+
20 # dead code
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
EOF
|
|
74
|
+
g.should have_error(Laser::DeadCodeWarning).on_line(6)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ControlFlow::UnusedVariables do
|
|
4
|
+
it 'should find a simple unused variable' do
|
|
5
|
+
g = cfg_method <<-EOF
|
|
6
|
+
def foo(x)
|
|
7
|
+
y = gets() * 2
|
|
8
|
+
z = y
|
|
9
|
+
c = z * z
|
|
10
|
+
end
|
|
11
|
+
EOF
|
|
12
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b c \b/x)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should find a more complex unused variable showing off ssa' do
|
|
16
|
+
g = cfg_method <<-EOF
|
|
17
|
+
def foo(x)
|
|
18
|
+
z = gets * 10
|
|
19
|
+
if z.size > 50
|
|
20
|
+
y = z # this y is used as return value
|
|
21
|
+
else
|
|
22
|
+
y = z * 2
|
|
23
|
+
puts y
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
EOF
|
|
27
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b y \b/x)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'should see when a variable is assigned and used to compute only unused vars' do
|
|
31
|
+
g = cfg_method <<-EOF
|
|
32
|
+
def foo(x)
|
|
33
|
+
z = gets * 10
|
|
34
|
+
d = z * 2
|
|
35
|
+
a = d
|
|
36
|
+
puts z
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
EOF
|
|
40
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(3).with_message(/\b d \b/x)
|
|
41
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b a \b/x)
|
|
42
|
+
g.should_not have_error(Laser::UnusedVariableWarning).with_message(/\b z \b/x)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should ignore SSA variables assigned and used to compute only unused vars' do
|
|
46
|
+
g = cfg_method <<-EOF
|
|
47
|
+
def foo(x)
|
|
48
|
+
z = gets * 10
|
|
49
|
+
if z.size > 3
|
|
50
|
+
d = z * 2
|
|
51
|
+
c = z
|
|
52
|
+
else
|
|
53
|
+
d = z * 10
|
|
54
|
+
c = 30
|
|
55
|
+
end
|
|
56
|
+
j = d
|
|
57
|
+
c
|
|
58
|
+
end
|
|
59
|
+
EOF
|
|
60
|
+
|
|
61
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b d \b/x)
|
|
62
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(7).with_message(/\b d \b/x)
|
|
63
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(10).with_message(/\b j \b/x)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'is improved by constant propagation' do
|
|
67
|
+
g = cfg_method <<-EOF
|
|
68
|
+
def foo(x)
|
|
69
|
+
z = (10 ** 5).to_s(5)
|
|
70
|
+
if z == "11200000"
|
|
71
|
+
d = z * 2
|
|
72
|
+
c = z
|
|
73
|
+
else
|
|
74
|
+
d = z * 10
|
|
75
|
+
c = 30
|
|
76
|
+
end
|
|
77
|
+
j = d
|
|
78
|
+
c
|
|
79
|
+
end
|
|
80
|
+
EOF
|
|
81
|
+
|
|
82
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b d \b/x)
|
|
83
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(7).with_message(/\b d \b/x)
|
|
84
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(8).with_message(/\b c \b/x)
|
|
85
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(10).with_message(/\b j \b/x)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'recognizes unused variables introduced through multiple assignment' do
|
|
89
|
+
g = cfg_method <<-EOF
|
|
90
|
+
def foo(x)
|
|
91
|
+
y = gets() * 2
|
|
92
|
+
z, i = y, 3
|
|
93
|
+
c = z * z
|
|
94
|
+
end
|
|
95
|
+
EOF
|
|
96
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(3).with_message(/\b i \b/x)
|
|
97
|
+
g.should have_error(Laser::UnusedVariableWarning).on_line(4).with_message(/\b c \b/x)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ControlFlow::YieldProperties do
|
|
4
|
+
it 'should recognize non-yielding methods' do
|
|
5
|
+
g = cfg_method <<-EOF
|
|
6
|
+
def foo(x)
|
|
7
|
+
y = gets() * 2
|
|
8
|
+
z = y
|
|
9
|
+
c = z * z
|
|
10
|
+
end
|
|
11
|
+
EOF
|
|
12
|
+
g.yield_type.should be :ignored
|
|
13
|
+
g.yield_arity.should == Set[]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should recognize non-yielding methods via CP' do
|
|
17
|
+
g = cfg_method <<-EOF
|
|
18
|
+
def foo(x)
|
|
19
|
+
x = 2 ** 16
|
|
20
|
+
if x == 65536
|
|
21
|
+
puts x
|
|
22
|
+
else
|
|
23
|
+
yield
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
EOF
|
|
27
|
+
g.yield_type.should be :ignored
|
|
28
|
+
g.yield_arity.should == Set[]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should recognize simple required-yield methods' do
|
|
32
|
+
g = cfg_method <<-EOF
|
|
33
|
+
def tap
|
|
34
|
+
yield self
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
EOF
|
|
38
|
+
g.yield_type.should be :required
|
|
39
|
+
g.yield_arity.should == Set[1]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'denotes the method required when a branch is unprovable' do
|
|
43
|
+
g = cfg_method <<-EOF
|
|
44
|
+
def one
|
|
45
|
+
if gets.size < 0
|
|
46
|
+
yield
|
|
47
|
+
else
|
|
48
|
+
1
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
EOF
|
|
52
|
+
g.yield_type.should be :required
|
|
53
|
+
g.yield_arity.should == Set[0]
|
|
54
|
+
end
|
|
55
|
+
['block_given?', 'defined?(yield)', 'defined?(yield($., *$*))', 'Proc.new', 'iterator?'].each do |guard|
|
|
56
|
+
it "denotes the method optional when yield is guarded by #{guard}" do
|
|
57
|
+
g = cfg_method <<-EOF
|
|
58
|
+
def one
|
|
59
|
+
if #{guard}
|
|
60
|
+
yield 1
|
|
61
|
+
else
|
|
62
|
+
1
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
EOF
|
|
66
|
+
g.yield_type.should be :optional
|
|
67
|
+
g.yield_arity.should == Set[1]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "denotes the method foolish when yield is not guarded by #{guard}, but the block is unused when given" do
|
|
71
|
+
g = cfg_method <<-EOF
|
|
72
|
+
def one
|
|
73
|
+
if #{guard}
|
|
74
|
+
1
|
|
75
|
+
else
|
|
76
|
+
yield 1
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
EOF
|
|
80
|
+
g.yield_type.should be :foolish
|
|
81
|
+
g.yield_arity.should == Set[]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'denotes the method optional when the explicit block arg is checked vs. nil' do
|
|
86
|
+
g = cfg_method <<-EOF
|
|
87
|
+
def one(&blk)
|
|
88
|
+
if blk != nil
|
|
89
|
+
yield 1
|
|
90
|
+
yield(1, 2)
|
|
91
|
+
else
|
|
92
|
+
1
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
EOF
|
|
96
|
+
g.yield_type.should be :optional
|
|
97
|
+
g.yield_arity.should == Set[1, 2]
|
|
98
|
+
end
|
|
99
|
+
[['.call(', ')'], ['[', ']'], ['.===(', ')']].each do |prefix, suffix|
|
|
100
|
+
it "denotes the method optional when the explicit block arg is checked vs. nil and called with #{prefix}#{suffix}" do
|
|
101
|
+
g = cfg_method <<-EOF
|
|
102
|
+
def one(&blk)
|
|
103
|
+
if blk != nil
|
|
104
|
+
blk#{prefix}2, 3#{suffix}
|
|
105
|
+
else
|
|
106
|
+
1
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
EOF
|
|
110
|
+
g.yield_type.should be :optional
|
|
111
|
+
g.yield_arity.should == Set[2]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "denotes the method required when the explicit block arg is not checked vs. nil and called with #{prefix}#{suffix}" do
|
|
115
|
+
g = cfg_method <<-EOF
|
|
116
|
+
def one(&blk)
|
|
117
|
+
blk#{prefix}2, 3#{suffix}
|
|
118
|
+
1
|
|
119
|
+
end
|
|
120
|
+
EOF
|
|
121
|
+
g.yield_type.should be :required
|
|
122
|
+
g.yield_arity.should == Set[2]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "denotes the method foolish when the explicit block arg is checked vs. nil and called with #{prefix}#{suffix}" do
|
|
126
|
+
g = cfg_method <<-EOF
|
|
127
|
+
def one(&blk)
|
|
128
|
+
if blk == nil
|
|
129
|
+
blk#{prefix}2, 3#{suffix}
|
|
130
|
+
end
|
|
131
|
+
1
|
|
132
|
+
end
|
|
133
|
+
EOF
|
|
134
|
+
g.yield_type.should be :foolish
|
|
135
|
+
g.yield_arity.should == Set[]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "denotes the method optional when the Proc.new block arg is checked vs. nil and called with #{prefix}#{suffix}" do
|
|
139
|
+
g = cfg_method <<-EOF
|
|
140
|
+
def one
|
|
141
|
+
blk = Proc::new
|
|
142
|
+
if blk != nil
|
|
143
|
+
blk#{prefix}2, 3#{suffix}
|
|
144
|
+
else
|
|
145
|
+
1
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
EOF
|
|
149
|
+
g.yield_type.should be :optional
|
|
150
|
+
g.yield_arity.should == Set[2]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "denotes the method required when the Proc.new block arg is not checked vs. nil and called with #{prefix}#{suffix}" do
|
|
154
|
+
g = cfg_method <<-EOF
|
|
155
|
+
def one
|
|
156
|
+
blk = Proc::new
|
|
157
|
+
blk#{prefix}2, 3#{suffix}
|
|
158
|
+
1
|
|
159
|
+
end
|
|
160
|
+
EOF
|
|
161
|
+
g.yield_type.should be :required
|
|
162
|
+
g.yield_arity.should == Set[2]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "denotes the method foolish when the Proc.new block arg is checked vs. nil and called with #{prefix}#{suffix}" do
|
|
166
|
+
g = cfg_method <<-EOF
|
|
167
|
+
def one
|
|
168
|
+
blk = Proc::new
|
|
169
|
+
if blk == nil
|
|
170
|
+
blk#{prefix}2, 3#{suffix}
|
|
171
|
+
end
|
|
172
|
+
1
|
|
173
|
+
end
|
|
174
|
+
EOF
|
|
175
|
+
g.yield_type.should be :foolish
|
|
176
|
+
g.yield_arity.should == Set[]
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'denotes the method ignored when the explicit block arg is never called' do
|
|
180
|
+
g = cfg_method <<-EOF
|
|
181
|
+
def one(&blk)
|
|
182
|
+
result = blk.nil? ? 5 : 10
|
|
183
|
+
result ** result
|
|
184
|
+
end
|
|
185
|
+
EOF
|
|
186
|
+
g.yield_type.should be :ignored
|
|
187
|
+
g.yield_arity.should == Set[]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "is not confused by sending #{prefix}#{suffix} to other arguments" do
|
|
191
|
+
g = cfg_method <<-EOF
|
|
192
|
+
def one(other_arg, &blk)
|
|
193
|
+
other_arg#{prefix}5#{suffix}
|
|
194
|
+
end
|
|
195
|
+
EOF
|
|
196
|
+
g.yield_type.should be :ignored
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it "uses SSA information to find aliases to the block that receive #{prefix}#{suffix}" do
|
|
200
|
+
g = cfg_method <<-EOF
|
|
201
|
+
def one(other_arg, &blk)
|
|
202
|
+
if gets.size > 2
|
|
203
|
+
other_arg = blk
|
|
204
|
+
end
|
|
205
|
+
other_arg#{prefix}2#{suffix}
|
|
206
|
+
end
|
|
207
|
+
EOF
|
|
208
|
+
g.yield_type.should be :required
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
%w(LocalJumpError StandardError Exception Object Kernel BasicObject).each do |exc|
|
|
213
|
+
it "denotes the method optional when yield is guarded by rescue of #{exc}" do
|
|
214
|
+
g = cfg_method <<-EOF
|
|
215
|
+
def one
|
|
216
|
+
yield 1
|
|
217
|
+
4
|
|
218
|
+
rescue #{exc}
|
|
219
|
+
2
|
|
220
|
+
end
|
|
221
|
+
EOF
|
|
222
|
+
g.yield_type.should be :optional
|
|
223
|
+
g.yield_arity.should == Set[1]
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "denotes the method optional when yield is guarded by rescue with no handlers" do
|
|
228
|
+
g = cfg_method <<-EOF
|
|
229
|
+
def one
|
|
230
|
+
yield 1
|
|
231
|
+
4
|
|
232
|
+
rescue
|
|
233
|
+
2
|
|
234
|
+
end
|
|
235
|
+
EOF
|
|
236
|
+
g.yield_type.should be :optional
|
|
237
|
+
g.yield_arity.should == Set[1]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it 'denotes the method optional when yield is guarded by a rescue modifier' do
|
|
241
|
+
g = cfg_method <<-EOF
|
|
242
|
+
def one
|
|
243
|
+
yield 1 rescue 2
|
|
244
|
+
end
|
|
245
|
+
EOF
|
|
246
|
+
g.yield_type.should be :optional
|
|
247
|
+
g.yield_arity.should == Set[1]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
it "denotes the method required if the yield is guarded by a non-matching rescue" do
|
|
251
|
+
g = cfg_method <<-EOF
|
|
252
|
+
def one
|
|
253
|
+
yield 1
|
|
254
|
+
4
|
|
255
|
+
rescue RuntimeError
|
|
256
|
+
2
|
|
257
|
+
end
|
|
258
|
+
EOF
|
|
259
|
+
g.yield_type.should be :required
|
|
260
|
+
g.yield_arity.should == Set[1]
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it 'infers yield likelihood with to_proc block syntax' do
|
|
264
|
+
cfg <<-EOF
|
|
265
|
+
class YP1
|
|
266
|
+
def foo(x)
|
|
267
|
+
yield x if block_given?
|
|
268
|
+
end
|
|
269
|
+
def bar(y, &blk)
|
|
270
|
+
foo(y, &blk)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
EOF
|
|
274
|
+
method = ClassRegistry['YP1'].instance_method(:bar)
|
|
275
|
+
method.yield_type.should be :optional
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
it 'infers yield likelihood with to_proc to a builtin method' do
|
|
279
|
+
cfg <<-EOF
|
|
280
|
+
class YP2
|
|
281
|
+
def bar(x, &blk)
|
|
282
|
+
[1, 2, x].each(&blk)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
EOF
|
|
286
|
+
method = ClassRegistry['YP2'].instance_method(:bar)
|
|
287
|
+
method.yield_type.should be :optional
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
it 'proves optionality in a complex example of yield likelihood through classes and ivars' do
|
|
291
|
+
cfg <<-EOF
|
|
292
|
+
class YP3
|
|
293
|
+
def initialize(data)
|
|
294
|
+
@data = data
|
|
295
|
+
end
|
|
296
|
+
def each(&blk)
|
|
297
|
+
@data.each(&blk)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
class YP4
|
|
301
|
+
def initialize(data)
|
|
302
|
+
@data = data
|
|
303
|
+
end
|
|
304
|
+
def foobar(&blk)
|
|
305
|
+
@data.each(&blk)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
YP4Temp1 = YP4.new(YP3.new([1, 2, 3]))
|
|
309
|
+
YP4Temp2 = YP4.new(YP3.new({a: :b, c: :d}))
|
|
310
|
+
YP4Temp3 = YP4.new([5, 6])
|
|
311
|
+
EOF
|
|
312
|
+
method = ClassRegistry['YP4'].instance_method(:foobar)
|
|
313
|
+
method.yield_type.should be :optional
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
it 'infers a complex example of yield likelihood through classes and ivars' do
|
|
317
|
+
cfg <<-EOF
|
|
318
|
+
class YP5
|
|
319
|
+
def each
|
|
320
|
+
yield 1
|
|
321
|
+
yield 2
|
|
322
|
+
yield 3
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
class YP6
|
|
326
|
+
def initialize(data)
|
|
327
|
+
@data = data
|
|
328
|
+
end
|
|
329
|
+
def foobar(&blk)
|
|
330
|
+
@data.each(&blk)
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
YP6Temp1 = YP6.new(YP5.new)
|
|
334
|
+
EOF
|
|
335
|
+
method = ClassRegistry['YP6'].instance_method(:foobar)
|
|
336
|
+
method.yield_type.should be :required
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
it 'infers yield likelihood through super-delegation' do
|
|
340
|
+
cfg <<-EOF
|
|
341
|
+
class YP7
|
|
342
|
+
def each
|
|
343
|
+
yield 1 if block_given?
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
class YP8 < YP7
|
|
347
|
+
def each(&blk)
|
|
348
|
+
super(&blk)
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
EOF
|
|
352
|
+
method = ClassRegistry['YP8'].instance_method(:each)
|
|
353
|
+
method.yield_type.should be :optional
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
it 'infers yield likelihood through zsuper-delegation' do
|
|
357
|
+
cfg <<-EOF
|
|
358
|
+
class YP9
|
|
359
|
+
def each
|
|
360
|
+
yield 1 if block_given?
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
class YP10 < YP9
|
|
364
|
+
def each
|
|
365
|
+
super
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
EOF
|
|
369
|
+
method = ClassRegistry['YP10'].instance_method(:each)
|
|
370
|
+
method.yield_type.should be :optional
|
|
371
|
+
end
|
|
372
|
+
end
|