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,63 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
require 'ostruct'
|
|
3
|
+
|
|
4
|
+
describe ProtocolRegistry do
|
|
5
|
+
extend AnalysisHelpers
|
|
6
|
+
clean_registry
|
|
7
|
+
|
|
8
|
+
before(:each) do
|
|
9
|
+
ProtocolRegistry.class_protocols = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#add_class' do
|
|
13
|
+
it 'adds a protocol to the main protocol list, and adds a shortcut in the class map' do
|
|
14
|
+
x = OpenStruct.new
|
|
15
|
+
x.path = 'SuperPath'
|
|
16
|
+
ProtocolRegistry.add_class x
|
|
17
|
+
ProtocolRegistry.class_protocols['SuperPath'].should == x
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe '#[]' do
|
|
22
|
+
it 'looks up quick queries by class path' do
|
|
23
|
+
x = OpenStruct.new
|
|
24
|
+
x.path = 'SuperPath'
|
|
25
|
+
ProtocolRegistry.add_class x
|
|
26
|
+
ProtocolRegistry['SuperPath'].should == [x]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe 'ClassRegistry' do
|
|
32
|
+
extend AnalysisHelpers
|
|
33
|
+
clean_registry
|
|
34
|
+
|
|
35
|
+
describe '#[]' do
|
|
36
|
+
it 'finds classes with the given name' do
|
|
37
|
+
ClassRegistry['Object'].should == ProtocolRegistry['Object'].first
|
|
38
|
+
x = OpenStruct.new
|
|
39
|
+
x.path = 'SillyWilly'
|
|
40
|
+
ProtocolRegistry.add_class x
|
|
41
|
+
ClassRegistry['SillyWilly'].should == x
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'raises on failure' do
|
|
45
|
+
expect { ClassRegistry['Hiybbprqag'] }.to raise_error(ArgumentError)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe 'built-in classes' do
|
|
50
|
+
it 'sets up Module, Class, and Object as instances of Class correctly' do
|
|
51
|
+
ClassRegistry['BasicObject'].binding.class_used.should == ClassRegistry['BasicObject'].singleton_class
|
|
52
|
+
ClassRegistry['Object'].binding.class_used.should == ClassRegistry['Object'].singleton_class
|
|
53
|
+
ClassRegistry['Module'].binding.class_used.should == ClassRegistry['Module'].singleton_class
|
|
54
|
+
ClassRegistry['Class'].binding.class_used.should == ClassRegistry['Class'].singleton_class
|
|
55
|
+
end
|
|
56
|
+
it "sets up Module, Class, and Object's hierarchy" do
|
|
57
|
+
ClassRegistry['BasicObject'].superclass.should == nil
|
|
58
|
+
ClassRegistry['Object'].superclass.should == ClassRegistry['BasicObject']
|
|
59
|
+
ClassRegistry['Module'].superclass.should == ClassRegistry['Object']
|
|
60
|
+
ClassRegistry['Class'].superclass.should == ClassRegistry['Module']
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
require_relative 'spec_helper'
|
|
2
|
+
require 'set'
|
|
3
|
+
describe 'general analyses' do
|
|
4
|
+
extend AnalysisHelpers
|
|
5
|
+
clean_registry
|
|
6
|
+
|
|
7
|
+
it_should_behave_like 'an annotator'
|
|
8
|
+
|
|
9
|
+
it 'adds the #scope method to Sexp' do
|
|
10
|
+
Sexp.instance_methods.should include(:scope)
|
|
11
|
+
end
|
|
12
|
+
# This is the AST that Ripper generates for the parsed code. It is
|
|
13
|
+
# provided here because otherwise the test is inscrutable.
|
|
14
|
+
#
|
|
15
|
+
# [:program,
|
|
16
|
+
# [[:module,
|
|
17
|
+
# [:const_ref, [:@const, "M13", [1, 7]]],
|
|
18
|
+
# [:bodystmt,
|
|
19
|
+
# [[:void_stmt],
|
|
20
|
+
# [:def,
|
|
21
|
+
# [:@ident, "silly", [1, 16]],
|
|
22
|
+
# [:paren,
|
|
23
|
+
# [:params, nil, nil, [:rest_param, [:@ident, "rest", [1, 23]]], nil, nil]],
|
|
24
|
+
# [:bodystmt,
|
|
25
|
+
# [[:void_stmt],
|
|
26
|
+
# [:command,
|
|
27
|
+
# [:@ident, "p", [1, 30]],
|
|
28
|
+
# [:args_add_block, [[:var_ref, [:@ident, "rest", [1, 32]]]], false]]],
|
|
29
|
+
# nil, nil, nil]]],
|
|
30
|
+
# nil, nil, nil]]]]
|
|
31
|
+
it 'defines methods on the current Module, if inside a module lexically' do
|
|
32
|
+
tree = annotate_all('module M13; def silly(*rest); p rest; end; end; class C13; include M13; end')
|
|
33
|
+
# now make sure the method got created in the M13 module!
|
|
34
|
+
method = ClassRegistry['M13'].instance_method(:silly)
|
|
35
|
+
method.should_not be_nil
|
|
36
|
+
rest = method.arguments[0]
|
|
37
|
+
rest.name.should == 'rest'
|
|
38
|
+
rest.kind.should == :rest
|
|
39
|
+
method.name.should == 'silly'
|
|
40
|
+
tree.all_errors.should be_empty
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# [:program,
|
|
44
|
+
# [[:module,
|
|
45
|
+
# [:const_ref, [:@const, "M49", [1, 7]]],
|
|
46
|
+
# [:bodystmt,
|
|
47
|
+
# [[:void_stmt],
|
|
48
|
+
# [:defs,
|
|
49
|
+
# [:var_ref, [:@kw, "self", [1, 16]]],
|
|
50
|
+
# [:@period, ".", [1, 20]],
|
|
51
|
+
# [:@ident, "silly", [1, 21]],
|
|
52
|
+
# [:paren,
|
|
53
|
+
# [:params,
|
|
54
|
+
# [[:@ident, "a", [1, 27]]],
|
|
55
|
+
# [[[:@ident, "b", [1, 30]], [:var_ref, [:@ident, "a", [1, 32]]]]],
|
|
56
|
+
# nil, nil, nil]],
|
|
57
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
58
|
+
# nil, nil, nil]]]]
|
|
59
|
+
it "allows singleton method declarations on a Module's self" do
|
|
60
|
+
tree = annotate_all('module M49; def self.silly(a, b=a); end; end')
|
|
61
|
+
|
|
62
|
+
method = ClassRegistry['M49'].singleton_class.instance_method(:silly)
|
|
63
|
+
method.should_not be_nil
|
|
64
|
+
a = method.arguments[0]
|
|
65
|
+
a.name.should == 'a'
|
|
66
|
+
a.kind.should == :positional
|
|
67
|
+
b = method.arguments[1]
|
|
68
|
+
b.name.should == 'b'
|
|
69
|
+
b.kind.should == :optional
|
|
70
|
+
method.name.should == 'silly'
|
|
71
|
+
tree.all_errors.should be_empty
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# [:program,
|
|
75
|
+
# [[:module,
|
|
76
|
+
# [:const_ref, [:@const, "M49", [1, 7]]],
|
|
77
|
+
# [:bodystmt,
|
|
78
|
+
# [[:void_stmt],
|
|
79
|
+
# [:sclass,
|
|
80
|
+
# [:var_ref, [:@kw, "self", [1, 21]]],
|
|
81
|
+
# [:bodystmt,
|
|
82
|
+
# [[:def,
|
|
83
|
+
# [:@ident, "silly", [1, 31]],
|
|
84
|
+
# [:paren,
|
|
85
|
+
# [:params,
|
|
86
|
+
# [[:@ident, "a", [1, 37]]],
|
|
87
|
+
# [[[:@ident, "b", [1, 40]], [:var_ref, [:@ident, "a", [1, 42]]]]],
|
|
88
|
+
# nil, nil, nil]],
|
|
89
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
90
|
+
# nil, nil, nil]]],
|
|
91
|
+
# nil, nil, nil]]]]
|
|
92
|
+
it "allows singleton method declarations on a Module's self using sclass opening" do
|
|
93
|
+
tree = annotate_all('module M50; class << self; def silly(a, b=a); end; end; end')
|
|
94
|
+
|
|
95
|
+
method = ClassRegistry['M50'].singleton_class.instance_method(:silly)
|
|
96
|
+
method.should_not be_nil
|
|
97
|
+
a = method.arguments[0]
|
|
98
|
+
a.name.should == 'a'
|
|
99
|
+
a.kind.should == :positional
|
|
100
|
+
b = method.arguments[1]
|
|
101
|
+
b.name.should == 'b'
|
|
102
|
+
b.kind.should == :optional
|
|
103
|
+
method.name.should == 'silly'
|
|
104
|
+
tree.all_errors.should be_empty
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# [:program,
|
|
108
|
+
# [[:class,
|
|
109
|
+
# [:const_ref, [:@const, "C51", [1, 6]]],
|
|
110
|
+
# nil,
|
|
111
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
|
|
112
|
+
# [:sclass,
|
|
113
|
+
# [:var_ref, [:@const, "C51", [1, 25]]],
|
|
114
|
+
# [:bodystmt,
|
|
115
|
+
# [[:def,
|
|
116
|
+
# [:@ident, "silly", [1, 34]],
|
|
117
|
+
# [:paren,
|
|
118
|
+
# [:params,
|
|
119
|
+
# [[:@ident, "a", [1, 40]]],
|
|
120
|
+
# [[[:@ident, "b", [1, 43]], [:var_ref, [:@ident, "a", [1, 45]]]]],
|
|
121
|
+
# nil, nil, nil]],
|
|
122
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
123
|
+
# nil, nil, nil]]]]
|
|
124
|
+
|
|
125
|
+
it "allows singleton method declarations on a Module's self using sclass opening" do
|
|
126
|
+
tree = annotate_all('class C51; end; class << C51; def silly(a, b=a); end; end')
|
|
127
|
+
|
|
128
|
+
method = ClassRegistry['C51'].singleton_class.instance_method(:silly)
|
|
129
|
+
method.should_not be_nil
|
|
130
|
+
a = method.arguments[0]
|
|
131
|
+
a.name.should == 'a'
|
|
132
|
+
a.kind.should == :positional
|
|
133
|
+
b = method.arguments[1]
|
|
134
|
+
b.name.should == 'b'
|
|
135
|
+
b.kind.should == :optional
|
|
136
|
+
method.name.should == 'silly'
|
|
137
|
+
tree.all_errors.should be_empty
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# This is the AST that Ripper generates for the parsed code. It is
|
|
141
|
+
# provided here because otherwise the test is inscrutable.
|
|
142
|
+
#
|
|
143
|
+
# sexp = [:program, [
|
|
144
|
+
# [:class, [:const_ref, [:@const, "C89", [1, 6]]], nil,
|
|
145
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
|
|
146
|
+
# [:class, [:const_ref, [:@const, "CPP", [1, 22]]], [:var_ref, [:@const, "C89", [1, 28]]],
|
|
147
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
|
|
148
|
+
it 'creates a new class with the appropriate superclass when specified' do
|
|
149
|
+
tree = annotate_all('class C89; end; class CPP < C89; end')
|
|
150
|
+
a, b = ClassRegistry['C89'], ClassRegistry['CPP']
|
|
151
|
+
|
|
152
|
+
a.klass.path.should == 'Class'
|
|
153
|
+
a.path.should == 'C89'
|
|
154
|
+
a.superclass.should == ClassRegistry['Object']
|
|
155
|
+
b.klass.path.should == 'Class'
|
|
156
|
+
b.path.should == 'CPP'
|
|
157
|
+
b.superclass.should == a
|
|
158
|
+
tree.all_errors.should be_empty
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# This is the AST that Ripper generates for the parsed code. It is
|
|
162
|
+
# provided here because otherwise the test is inscrutable.
|
|
163
|
+
#
|
|
164
|
+
# sexp = [:program,
|
|
165
|
+
# [[:module, [:const_ref, [:@const, "WWD", [1, 7]]],
|
|
166
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
|
|
167
|
+
# [:class,
|
|
168
|
+
# [:const_path_ref, [:var_ref, [:@const, "WWD", [1, 23]]],
|
|
169
|
+
# [:@const, "SuperModule", [1, 28]]],
|
|
170
|
+
# [:var_ref, [:@const, "Module", [1, 42]]],
|
|
171
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
|
|
172
|
+
it 'declares classes inside modules with path-based definitions' do
|
|
173
|
+
tree = annotate_all('module WWD; end; class WWD::SuperModule < Module; end')
|
|
174
|
+
|
|
175
|
+
mod = ClassRegistry['WWD']
|
|
176
|
+
mod.should be_a(LaserModule)
|
|
177
|
+
supermod = ClassRegistry['WWD::SuperModule']
|
|
178
|
+
supermod.should be_a(LaserClass)
|
|
179
|
+
supermod.superclass.should == ClassRegistry['Module']
|
|
180
|
+
tree.all_errors.should be_empty
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# [:program,
|
|
184
|
+
# [[:class,
|
|
185
|
+
# [:const_ref, [:@const, "Alpha", [1, 6]]],
|
|
186
|
+
# nil,
|
|
187
|
+
# [:bodystmt,
|
|
188
|
+
# [[:def,
|
|
189
|
+
# [:@ident, "do_xyz", [1, 17]],
|
|
190
|
+
# [:paren,
|
|
191
|
+
# [:params,
|
|
192
|
+
# [[:@ident, "a", [1, 24]]],
|
|
193
|
+
# [[[:@ident, "b", [1, 27]], [:var_ref, [:@ident, "a", [1, 29]]]]],
|
|
194
|
+
# nil, nil, nil]],
|
|
195
|
+
# [:bodystmt,
|
|
196
|
+
# [[:void_stmt],
|
|
197
|
+
# [:command,
|
|
198
|
+
# [:@ident, "p", [1, 33]],
|
|
199
|
+
# [:args_add_block, [[:var_ref, [:@ident, "b", [1, 35]]]], false]]],
|
|
200
|
+
# nil, nil, nil]]],
|
|
201
|
+
# nil, nil, nil]],
|
|
202
|
+
# [:class,
|
|
203
|
+
# [:const_ref, [:@const, "B22", [1, 54]]],
|
|
204
|
+
# [:var_ref, [:@const, "Alpha", [1, 60]]],
|
|
205
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]]]
|
|
206
|
+
it 'defines methods on the current Class, which are inherited' do
|
|
207
|
+
tree = annotate_all('class Alpha; def do_xyz(a, b=a); p b; end; end; class B22 < Alpha; end')
|
|
208
|
+
# now make sure the method got created in the M13 module!
|
|
209
|
+
['Alpha', 'B22'].each do |klass|
|
|
210
|
+
method = ClassRegistry[klass].instance_method(:do_xyz)
|
|
211
|
+
method.should_not be_nil
|
|
212
|
+
a = method.arguments[0]
|
|
213
|
+
a.name.should == 'a'
|
|
214
|
+
a.kind.should == :positional
|
|
215
|
+
b = method.arguments[1]
|
|
216
|
+
b.name.should == 'b'
|
|
217
|
+
b.kind.should == :optional
|
|
218
|
+
method.name.should == 'do_xyz'
|
|
219
|
+
end
|
|
220
|
+
tree.all_errors.should be_empty
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'removes methods via #remove_method' do
|
|
224
|
+
annotate_all('class RM1; def do_xyz(a); end; end')
|
|
225
|
+
ClassRegistry['RM1'].instance_method(:do_xyz).should be_a(LaserMethod)
|
|
226
|
+
annotate_all('class RM1; remove_method :do_xyz; end')
|
|
227
|
+
ClassRegistry['RM1'].instance_method(:do_xyz).should be nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it 'passes resolution to superclasses after #remove_method' do
|
|
231
|
+
annotate_all('class RM2; def do_xyz(a); end; end; class RMSub < RM2; def do_xyz(b); end; end')
|
|
232
|
+
ClassRegistry['RMSub'].instance_method(:do_xyz).should be_a(LaserMethod)
|
|
233
|
+
ClassRegistry['RMSub'].instance_method(:do_xyz).should_not ==
|
|
234
|
+
ClassRegistry['RM2'].instance_method(:do_xyz)
|
|
235
|
+
|
|
236
|
+
annotate_all('class RMSub; remove_method :do_xyz; end')
|
|
237
|
+
ClassRegistry['RMSub'].instance_method(:do_xyz).should ==
|
|
238
|
+
ClassRegistry['RM2'].instance_method(:do_xyz)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# [:program,
|
|
242
|
+
# [[:def,
|
|
243
|
+
# [:@ident, "abc", [1, 4]],
|
|
244
|
+
# [:paren,
|
|
245
|
+
# [:params,
|
|
246
|
+
# [[:@ident, "bar", [1, 8]]],
|
|
247
|
+
# nil, nil, nil,
|
|
248
|
+
# [:blockarg, [:@ident, "blk", [1, 14]]]]],
|
|
249
|
+
# [:bodystmt,
|
|
250
|
+
# [[:void_stmt],
|
|
251
|
+
# [:command,
|
|
252
|
+
# [:@ident, "p", [1, 20]],
|
|
253
|
+
# [:args_add_block, [[:var_ref, [:@ident, "blk", [1, 22]]]], false]]],
|
|
254
|
+
# nil, nil, nil]]]]
|
|
255
|
+
it 'defines method on the main object, if no scope is otherwise enclosing a method definition' do
|
|
256
|
+
tree = annotate_all('def abce(bar, &blk); p blk; end')
|
|
257
|
+
method = Scope::GlobalScope.self_ptr.singleton_class.instance_method(:abce)
|
|
258
|
+
method.should_not be_nil
|
|
259
|
+
Scope::GlobalScope.self_ptr.singleton_class.visibility_table[:abce].should == :private
|
|
260
|
+
bar = method.arguments[0]
|
|
261
|
+
bar.name.should == 'bar'
|
|
262
|
+
bar.kind.should == :positional
|
|
263
|
+
blk = method.arguments[1]
|
|
264
|
+
blk.name.should == 'blk'
|
|
265
|
+
blk.kind.should == :block
|
|
266
|
+
method.name.should == 'abce'
|
|
267
|
+
tree.all_errors.should be_empty
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# [:program,
|
|
271
|
+
# [[:defs,
|
|
272
|
+
# [:var_ref, [:@kw, "self", [1, 4]]],
|
|
273
|
+
# [:@period, ".", [1, 8]],
|
|
274
|
+
# [:@ident, "abc", [1, 9]],
|
|
275
|
+
# [:paren,
|
|
276
|
+
# [:params,
|
|
277
|
+
# [[:@ident, "bar", [1, 13]]],
|
|
278
|
+
# nil, nil, nil,
|
|
279
|
+
# [:blockarg, [:@ident, "blk", [1, 19]]]]],
|
|
280
|
+
# [:bodystmt,
|
|
281
|
+
# [[:void_stmt],
|
|
282
|
+
# [:command,
|
|
283
|
+
# [:@ident, "p", [1, 25]],
|
|
284
|
+
# [:args_add_block, [[:var_ref, [:@ident, "blk", [1, 27]]]], false]]],
|
|
285
|
+
# nil, nil, nil]]]]
|
|
286
|
+
it 'defines singleton methods on the main object, if no scope is otherwise enclosing a method definition' do
|
|
287
|
+
tree = annotate_all('def self.abcd(bar, &blk); p blk; end')
|
|
288
|
+
method = Scope::GlobalScope.self_ptr.singleton_class.instance_method(:abcd)
|
|
289
|
+
method.should_not be_nil
|
|
290
|
+
bar = method.arguments[0]
|
|
291
|
+
bar.name.should == 'bar'
|
|
292
|
+
bar.kind.should == :positional
|
|
293
|
+
blk = method.arguments[1]
|
|
294
|
+
blk.name.should == 'blk'
|
|
295
|
+
blk.kind.should == :block
|
|
296
|
+
method.name.should == 'abcd'
|
|
297
|
+
tree.all_errors.should be_empty
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# [:program,
|
|
301
|
+
# [[:module,
|
|
302
|
+
# [:const_ref, [:@const, "TestA", [1, 7]]],
|
|
303
|
+
# [:bodystmt,
|
|
304
|
+
# [[:void_stmt],
|
|
305
|
+
# [:assign,
|
|
306
|
+
# [:var_field, [:@const, "PI", [1, 14]]],
|
|
307
|
+
# [:@float, "3.14", [1, 19]]],
|
|
308
|
+
# [:assign,
|
|
309
|
+
# [:var_field, [:@const, "TAU", [1, 25]]],
|
|
310
|
+
# [:binary,
|
|
311
|
+
# [:var_ref, [:@const, "PI", [1, 31]]],
|
|
312
|
+
# :*,
|
|
313
|
+
# [:@int, "2", [1, 36]]]]],
|
|
314
|
+
# nil, nil, nil]]]]
|
|
315
|
+
it 'creates new constant bindings as new constants are assigned to' do
|
|
316
|
+
tree = annotate_all('module TestA; PI = 3.14; TAU = PI * 2; end')
|
|
317
|
+
|
|
318
|
+
ClassRegistry['TestA'].should be_a(LaserModule)
|
|
319
|
+
ClassRegistry['TestA'].const_get('PI').should_not be_nil
|
|
320
|
+
ClassRegistry['TestA'].const_get('TAU').should_not be_nil
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it 'handles module inclusions done in the typical method-call fashion' do
|
|
324
|
+
input = 'module A113; end; module B113; end; class C113; include A113, B113; end'
|
|
325
|
+
tree = annotate_all(input)
|
|
326
|
+
|
|
327
|
+
c113 = ClassRegistry['C113']
|
|
328
|
+
c113.should_not be nil
|
|
329
|
+
c113.ancestors.should == [ClassRegistry['C113'], ClassRegistry['A113'], ClassRegistry['B113'],
|
|
330
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
331
|
+
|
|
332
|
+
tree.all_errors.should be_empty
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it 'handles complex module/class hierarchies' do
|
|
336
|
+
input = "module A114; end; module B114; include A114; end; module C114; include B114; end\n" +
|
|
337
|
+
"class X114; include A114; end; class Y114 < X114; include C114; end"
|
|
338
|
+
tree = annotate_all(input)
|
|
339
|
+
|
|
340
|
+
ClassRegistry['A114'].ancestors.should == [ClassRegistry['A114']]
|
|
341
|
+
ClassRegistry['B114'].ancestors.should == [ClassRegistry['B114'], ClassRegistry['A114']]
|
|
342
|
+
ClassRegistry['C114'].ancestors.should == [ClassRegistry['C114'], ClassRegistry['B114'], ClassRegistry['A114']]
|
|
343
|
+
|
|
344
|
+
ClassRegistry['X114'].ancestors.should == [ClassRegistry['X114'], ClassRegistry['A114'], ClassRegistry['Object'],
|
|
345
|
+
ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
346
|
+
ClassRegistry['Y114'].ancestors.should == [ClassRegistry['Y114'], ClassRegistry['C114'], ClassRegistry['B114'],
|
|
347
|
+
ClassRegistry['X114'], ClassRegistry['A114'], ClassRegistry['Object'],
|
|
348
|
+
ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
349
|
+
tree.all_errors.should be_empty
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it 'generates an error when a class is re-opened as a module' do
|
|
353
|
+
input = "class A115; end; module A115; end"
|
|
354
|
+
tree = annotate_all(input)
|
|
355
|
+
|
|
356
|
+
tree.errors.should_not be_empty
|
|
357
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
358
|
+
err = tree.all_errors.first
|
|
359
|
+
err.error.normal_class.should == ClassRegistry['LaserReopenedClassAsModuleError']
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
it 'generates an error when a module is re-opened as a class' do
|
|
363
|
+
input = "module A116; end; class A116; end"
|
|
364
|
+
tree = annotate_all(input)
|
|
365
|
+
|
|
366
|
+
tree.errors.should_not be_empty
|
|
367
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
368
|
+
err = tree.all_errors.first
|
|
369
|
+
err.error.normal_class.should == ClassRegistry['LaserReopenedModuleAsClassError']
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it 'handles module inclusions done in the parenthesized method-call fashion' do
|
|
373
|
+
input = 'module A117; end; module B117; end; class C117; include(A117, B117); end'
|
|
374
|
+
tree = annotate_all(input)
|
|
375
|
+
|
|
376
|
+
c117 = ClassRegistry['C117']
|
|
377
|
+
c117.should_not be nil
|
|
378
|
+
c117.ancestors.should == [ClassRegistry['C117'], ClassRegistry['A117'],
|
|
379
|
+
ClassRegistry['B117'], ClassRegistry['Object'],
|
|
380
|
+
ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
381
|
+
|
|
382
|
+
tree.all_errors.should be_empty
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it 'reports an error when a module is included unnecessarily' do
|
|
386
|
+
input = 'module A240; end; class B240; include A240; end; class C240 < B240; include A240; end'
|
|
387
|
+
tree = annotate_all(input)
|
|
388
|
+
|
|
389
|
+
c240 = ClassRegistry['C240']
|
|
390
|
+
c240.should_not be nil
|
|
391
|
+
c240.ancestors.should == [ClassRegistry['C240'], ClassRegistry['B240'],
|
|
392
|
+
ClassRegistry['A240'], ClassRegistry['Object'],
|
|
393
|
+
ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
394
|
+
tree.all_errors.should_not be_empty
|
|
395
|
+
tree.all_errors.size.should == 1
|
|
396
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
397
|
+
err = tree.all_errors.first
|
|
398
|
+
err.error.class.should == DoubleIncludeError
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
it 'handles module extensions done in the typical method-call fashion' do
|
|
402
|
+
input = 'module A118; end; module B118; end; class C118; extend A118, B118; end'
|
|
403
|
+
tree = annotate_all(input)
|
|
404
|
+
|
|
405
|
+
c118 = ClassRegistry['C118']
|
|
406
|
+
c118.should_not be nil
|
|
407
|
+
c118.singleton_class.ancestors.should == [ClassRegistry['C118'].singleton_class,
|
|
408
|
+
ClassRegistry['A118'], ClassRegistry['B118'],
|
|
409
|
+
ClassRegistry['Object'].singleton_class,
|
|
410
|
+
ClassRegistry['BasicObject'].singleton_class,
|
|
411
|
+
ClassRegistry['Class'], ClassRegistry['Module'],
|
|
412
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
413
|
+
ClassRegistry['BasicObject']]
|
|
414
|
+
tree.all_errors.should be_empty
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
it 'handles complex module/class extension hierarchies' do
|
|
418
|
+
input = "module A119; end; module B119; extend A119; end; module C119; extend B119; end\n" +
|
|
419
|
+
"class X119; extend A119; end; class Y119 < X119; extend C119; end"
|
|
420
|
+
tree = annotate_all(input)
|
|
421
|
+
|
|
422
|
+
ClassRegistry['A119'].singleton_class.ancestors.should ==
|
|
423
|
+
[ClassRegistry['A119'].singleton_class, ClassRegistry['Module'],
|
|
424
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'], ClassRegistry['BasicObject']]
|
|
425
|
+
ClassRegistry['B119'].singleton_class.ancestors.should ==
|
|
426
|
+
[ClassRegistry['B119'].singleton_class, ClassRegistry['A119'],
|
|
427
|
+
ClassRegistry['Module'], ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
428
|
+
ClassRegistry['BasicObject']]
|
|
429
|
+
ClassRegistry['C119'].singleton_class.ancestors.should ==
|
|
430
|
+
[ClassRegistry['C119'].singleton_class, ClassRegistry['B119'],
|
|
431
|
+
ClassRegistry['Module'], ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
432
|
+
ClassRegistry['BasicObject']]
|
|
433
|
+
|
|
434
|
+
ClassRegistry['X119'].singleton_class.ancestors.should ==
|
|
435
|
+
[ClassRegistry['X119'].singleton_class, ClassRegistry['A119'],
|
|
436
|
+
ClassRegistry['Object'].singleton_class,
|
|
437
|
+
ClassRegistry['BasicObject'].singleton_class,
|
|
438
|
+
ClassRegistry['Class'], ClassRegistry['Module'],
|
|
439
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
440
|
+
ClassRegistry['BasicObject']]
|
|
441
|
+
ClassRegistry['Y119'].singleton_class.ancestors.should ==
|
|
442
|
+
[ClassRegistry['Y119'].singleton_class, ClassRegistry['C119'],
|
|
443
|
+
ClassRegistry['X119'].singleton_class, ClassRegistry['A119'],
|
|
444
|
+
ClassRegistry['Object'].singleton_class,
|
|
445
|
+
ClassRegistry['BasicObject'].singleton_class,
|
|
446
|
+
ClassRegistry['Class'], ClassRegistry['Module'],
|
|
447
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
448
|
+
ClassRegistry['BasicObject']]
|
|
449
|
+
|
|
450
|
+
tree.all_errors.should be_empty
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it 'handles module extensions done in the parenthesized method-call fashion' do
|
|
454
|
+
input = 'module A120; end; module B120; end; class C120; extend(A120, B120); end'
|
|
455
|
+
tree = annotate_all(input)
|
|
456
|
+
|
|
457
|
+
c120 = ClassRegistry['C120']
|
|
458
|
+
c120.should_not be nil
|
|
459
|
+
c120.singleton_class.ancestors.should == [ClassRegistry['C120'].singleton_class,
|
|
460
|
+
ClassRegistry['A120'], ClassRegistry['B120'],
|
|
461
|
+
ClassRegistry['Object'].singleton_class,
|
|
462
|
+
ClassRegistry['BasicObject'].singleton_class,
|
|
463
|
+
ClassRegistry['Class'], ClassRegistry['Module'],
|
|
464
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
465
|
+
ClassRegistry['BasicObject']]
|
|
466
|
+
tree.all_errors.should be_empty
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# [:program,
|
|
470
|
+
# [[:class,
|
|
471
|
+
# [:const_ref, [:@const, "A121", [1, 6]]],
|
|
472
|
+
# nil,
|
|
473
|
+
# [:bodystmt,
|
|
474
|
+
# [[:assign, [:var_field, [:@ident, "x", [1, 12]]], [:@int, "10", [1, 16]]],
|
|
475
|
+
# [:assign,
|
|
476
|
+
# [:var_field, [:@ident, "y", [1, 20]]],
|
|
477
|
+
# [:var_ref, [:@ident, "z", [1, 24]]]]],
|
|
478
|
+
# nil, nil, nil]]]]
|
|
479
|
+
it 'should generate an error if a local variable cannot be found' do
|
|
480
|
+
input = 'class A121; x = 10; y = Z223; end'
|
|
481
|
+
tree = annotate_all(input)
|
|
482
|
+
|
|
483
|
+
errors = tree.all_errors
|
|
484
|
+
errors.should_not be_empty
|
|
485
|
+
errors.first.should be_a(TopLevelSimulationRaised)
|
|
486
|
+
errors.first.error.class.should == ArgumentError
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it 'switches to private visibility upon reaching a call to #private in a class/module' do
|
|
490
|
+
input = 'class A122; private; def foobar; end; end'
|
|
491
|
+
tree = annotate_all(input)
|
|
492
|
+
|
|
493
|
+
ClassRegistry['A122'].visibility_table[:foobar].should == :private
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it 'does not switch to private visibility if a local variable is called private' do
|
|
497
|
+
input = 'class A123; private = 5; private; def foobar; end; end'
|
|
498
|
+
tree = annotate_all(input)
|
|
499
|
+
|
|
500
|
+
ClassRegistry['A123'].visibility_table[:foobar].should == :public
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
it 'switches back and forth from public, private, and protected visibility in a class/module' do
|
|
504
|
+
input = 'module A124; def abc; end; private; def foobar; end; protected; def silly; end; private; def priv; end; end'
|
|
505
|
+
tree = annotate_all(input)
|
|
506
|
+
|
|
507
|
+
ClassRegistry['A124'].visibility_table[:abc].should == :public
|
|
508
|
+
ClassRegistry['A124'].visibility_table[:foobar].should == :private
|
|
509
|
+
ClassRegistry['A124'].visibility_table[:silly].should == :protected
|
|
510
|
+
ClassRegistry['A124'].visibility_table[:priv].should == :private
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
it 'switches to back visibility when re-opening the same class from within (complex edge case)' do
|
|
514
|
+
input = 'class E1; private; class ::E1; def foo; end; end; def bar; end; end'
|
|
515
|
+
tree = annotate_all(input)
|
|
516
|
+
|
|
517
|
+
ClassRegistry['E1'].visibility_table[:foo].should == :public
|
|
518
|
+
ClassRegistry['E1'].visibility_table[:bar].should == :private
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
it 'switches to private on module_function and back on public/protected' do
|
|
522
|
+
input = 'module A200; def abc; end; module_function; def foobar; end; protected;' +
|
|
523
|
+
'def silly; end; public; def priv; end; end'
|
|
524
|
+
tree = annotate_all(input)
|
|
525
|
+
|
|
526
|
+
ClassRegistry['A200'].visibility_table[:abc].should == :public
|
|
527
|
+
ClassRegistry['A200'].visibility_table[:foobar].should == :private
|
|
528
|
+
ClassRegistry['A200'].visibility_table[:silly].should == :protected
|
|
529
|
+
ClassRegistry['A200'].visibility_table[:priv].should == :public
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
it 'sets all module_function methods to private when specified as arguments' do
|
|
533
|
+
input = 'module A201; def abc; end; def foobar; end; def silly; end; module_function :abc, :silly; end'
|
|
534
|
+
tree = annotate_all(input)
|
|
535
|
+
|
|
536
|
+
ClassRegistry['A201'].visibility_table[:abc].should == :private
|
|
537
|
+
ClassRegistry['A201'].visibility_table[:foobar].should == :public
|
|
538
|
+
ClassRegistry['A201'].visibility_table[:silly].should == :private
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
it 'creates public singleton class methods when module_function is used with no args' do
|
|
542
|
+
input = 'module A202; def def; end; module_function; def foobar; "hi"; 3; end;' +
|
|
543
|
+
'def silly; /regex/; end; public; def priv; end; end'
|
|
544
|
+
tree = annotate_all(input)
|
|
545
|
+
|
|
546
|
+
ClassRegistry['A202'].singleton_class.visibility_table[:foobar].should == :public
|
|
547
|
+
ClassRegistry['A202'].singleton_class.visibility_table[:silly].should == :public
|
|
548
|
+
ClassRegistry['A202'].singleton_class.instance_method(:def).should be nil
|
|
549
|
+
ClassRegistry['A202'].singleton_class.instance_method(:priv).should be nil
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
it 'creates public singleton class methods when module_function is used with args' do
|
|
553
|
+
input = 'module A203; def def; end; def foobar; "hi"; 3; end;' +
|
|
554
|
+
'def silly; /regex/; end; public; def priv; end; module_function :foobar, :silly; end'
|
|
555
|
+
tree = annotate_all(input)
|
|
556
|
+
|
|
557
|
+
ClassRegistry['A203'].singleton_class.visibility_table[:foobar].should == :public
|
|
558
|
+
ClassRegistry['A203'].singleton_class.visibility_table[:silly].should == :public
|
|
559
|
+
ClassRegistry['A203'].singleton_class.instance_method(:def).should be nil
|
|
560
|
+
ClassRegistry['A203'].singleton_class.instance_method(:priv).should be nil
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
it 'uses a default private scope at the top level but can switch to public and private' do
|
|
564
|
+
input = 'def t11; end; public; def t12; end; private; def t13; end'
|
|
565
|
+
tree = annotate_all(input)
|
|
566
|
+
|
|
567
|
+
singleton = Scope::GlobalScope.self_ptr.singleton_class
|
|
568
|
+
singleton.visibility_table[:t11].should == :private
|
|
569
|
+
singleton.visibility_table[:t12].should == :public
|
|
570
|
+
singleton.visibility_table[:t13].should == :private
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
it 'raises an error if you try to use protected at the top level' do
|
|
574
|
+
input = 'def t14; end; protected; def t15; end; public; def t16; end'
|
|
575
|
+
tree = annotate_all(input)
|
|
576
|
+
tree.all_errors.size.should be 1
|
|
577
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
578
|
+
tree.all_errors.first.error.normal_class.should == ClassRegistry['NameError']
|
|
579
|
+
|
|
580
|
+
# recovers by not changing visibility
|
|
581
|
+
# singleton = Scope::GlobalScope.self_ptr.singleton_class
|
|
582
|
+
# singleton.visibility_table[:t14].should == :private
|
|
583
|
+
# singleton.visibility_table[:t15].should == :private
|
|
584
|
+
# singleton.visibility_table[:t16].should == :public
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
it 'allows specifying private/public/protected for individual methods at the class level' do
|
|
588
|
+
input = 'class A125; def t17; end; def t18; end; def t19; end; private *[:t17, :t19]; end'
|
|
589
|
+
tree = annotate_all(input)
|
|
590
|
+
|
|
591
|
+
ClassRegistry['A125'].visibility_table[:t17].should == :private
|
|
592
|
+
ClassRegistry['A125'].visibility_table[:t18].should == :public
|
|
593
|
+
ClassRegistry['A125'].visibility_table[:t19].should == :private
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
it 'allows specifying private/public/protected for individual methods at the top level' do
|
|
598
|
+
input = 'def t17; end; def t18; end; def t19; end; public *[:t17, :t19]'
|
|
599
|
+
tree = annotate_all(input)
|
|
600
|
+
|
|
601
|
+
# recovers by not changing visibility
|
|
602
|
+
singleton = Scope::GlobalScope.self_ptr.singleton_class
|
|
603
|
+
singleton.visibility_table[:t17].should == :public
|
|
604
|
+
singleton.visibility_table[:t18].should == :private
|
|
605
|
+
singleton.visibility_table[:t19].should == :public
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
it 'can resolve constant aliasing with superclasses' do
|
|
609
|
+
tree = annotate_all('class Alpha111; end; Beta111 = Alpha111; class B290 < Beta111; end')
|
|
610
|
+
|
|
611
|
+
ClassRegistry['B290'].superclass.should == ClassRegistry['Alpha111']
|
|
612
|
+
|
|
613
|
+
tree.all_errors.should be_empty
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
describe 'performing requires' do
|
|
617
|
+
before do
|
|
618
|
+
@load_paths = Scope::GlobalScope.lookup('$:').value
|
|
619
|
+
@features = Scope::GlobalScope.lookup('$"').value
|
|
620
|
+
@original = @load_paths.dup
|
|
621
|
+
@orig_features = @features.dup
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
after do
|
|
625
|
+
@load_paths.replace(@original)
|
|
626
|
+
@features.replace(@orig_features)
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
it 'should load the file from $: if it is not yet in $"' do
|
|
630
|
+
@load_paths.unshift('/abc/def')
|
|
631
|
+
File.should_receive(:exist?).with('/abc/def/foobaz.rb').and_return(true)
|
|
632
|
+
File.should_receive(:read).with('/abc/def/foobaz.rb').and_return('class Alpha112 < Hash;end')
|
|
633
|
+
annotate_all("require 'foobaz'")
|
|
634
|
+
ClassRegistry['Alpha112'].superclass.should == ClassRegistry['Hash']
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
it 'should check all paths in $: for the file in a row' do
|
|
638
|
+
@load_paths.unshift('/abc/def').unshift('/def/jkl').unshift('/jkl/uio')
|
|
639
|
+
File.should_receive(:exist?).with('/jkl/uio/foobaz.rb').and_return(false)
|
|
640
|
+
File.should_receive(:exist?).with('/def/jkl/foobaz.rb').and_return(false)
|
|
641
|
+
File.should_receive(:exist?).with('/abc/def/foobaz.rb').and_return(true)
|
|
642
|
+
File.should_receive(:read).with('/abc/def/foobaz.rb').and_return('class Alpha113 < Array;end')
|
|
643
|
+
annotate_all("require 'foobaz'")
|
|
644
|
+
ClassRegistry['Alpha113'].superclass.should == ClassRegistry['Array']
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
it 'should not load the file if it is found in $"' do
|
|
648
|
+
@load_paths.unshift('/abc/def').unshift('/def/jkl').unshift('/jkl/uio')
|
|
649
|
+
@features.unshift('/abc/def/foobaz.rb')
|
|
650
|
+
File.should_receive(:exist?).with('/jkl/uio/foobaz.rb').and_return(false)
|
|
651
|
+
File.should_receive(:exist?).with('/def/jkl/foobaz.rb').and_return(false)
|
|
652
|
+
File.should_receive(:exist?).with('/abc/def/foobaz.rb').and_return(true)
|
|
653
|
+
File.should_not_receive(:read)
|
|
654
|
+
annotate_all("require 'foobaz'")
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
it 'should raise a SuperclassMismatchError when an improper superclass is specified' do
|
|
659
|
+
input = 'class A250 < String; end; class A250 < Fixnum; end'
|
|
660
|
+
tree = annotate_all(input)
|
|
661
|
+
tree.all_errors.size.should be 1
|
|
662
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
663
|
+
tree.all_errors.first.error.normal_class.should == ClassRegistry['LaserSuperclassMismatchError']
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
it "should not raise a SuperclassMismatchError when BasicObject's superclass is omitted" do
|
|
667
|
+
input = 'class BasicObject; end'
|
|
668
|
+
tree = annotate_all(input)
|
|
669
|
+
tree.all_errors.should be_empty
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
it "should not raise a SuperclassMismatchError when BasicObject's superclass is nil" do
|
|
673
|
+
input = 'class BasicObject < nil; end'
|
|
674
|
+
tree = annotate_all(input)
|
|
675
|
+
tree.all_errors.should be_empty
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
it "should raise a SuperclassMismatchError when BasicObject's superclass is specified and not nil" do
|
|
679
|
+
input = 'class BasicObject < String; end'
|
|
680
|
+
tree = annotate_all(input)
|
|
681
|
+
tree.all_errors.size.should be 1
|
|
682
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
683
|
+
tree.all_errors.first.error.normal_class.should == ClassRegistry['LaserSuperclassMismatchError']
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
it "should not raise a SuperclassMismatchError when Object's superclass is omitted" do
|
|
687
|
+
input = 'class Object; end'
|
|
688
|
+
tree = annotate_all(input)
|
|
689
|
+
tree.all_errors.should be_empty
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
it "should not raise a SuperclassMismatchError when Object's superclass is BasicObject" do
|
|
693
|
+
input = 'class Object < BasicObject; end'
|
|
694
|
+
tree = annotate_all(input)
|
|
695
|
+
tree.all_errors.should be_empty
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
it "should raise a SuperclassMismatchError when Object's superclass is specified and not BasicObject" do
|
|
699
|
+
input = 'class Object < Array; end'
|
|
700
|
+
tree = annotate_all(input)
|
|
701
|
+
tree.all_errors.size.should be 1
|
|
702
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
703
|
+
tree.all_errors.first.error.normal_class.should == ClassRegistry['LaserSuperclassMismatchError']
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
it "should not raise a SuperclassMismatchError when Class's superclass is omitted" do
|
|
707
|
+
input = 'class Class; end'
|
|
708
|
+
tree = annotate_all(input)
|
|
709
|
+
tree.all_errors.should be_empty
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
it "should not raise a SuperclassMismatchError when Class's superclass is Module" do
|
|
713
|
+
input = 'class Class < Module; end'
|
|
714
|
+
tree = annotate_all(input)
|
|
715
|
+
tree.all_errors.should be_empty
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
it "should raise a SuperclassMismatchError when Class's superclass is specified and not Module" do
|
|
719
|
+
input = 'class Class < BasicObject; end'
|
|
720
|
+
tree = annotate_all(input)
|
|
721
|
+
tree.all_errors.size.should be 1
|
|
722
|
+
tree.all_errors.first.should be_a(TopLevelSimulationRaised)
|
|
723
|
+
tree.all_errors.first.error.normal_class.should == ClassRegistry['LaserSuperclassMismatchError']
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
it "should not raise a SuperclassMismatchError when a class is opened without it's Object superclass" do
|
|
727
|
+
input = 'class String; end'
|
|
728
|
+
tree = annotate_all(input)
|
|
729
|
+
tree.all_errors.should be_empty
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
it 'observes aliases and sets the corresponding instance methods correctly' do
|
|
733
|
+
input = 'class SA99; def foo; end; alias silly foo; end'
|
|
734
|
+
annotate_all(input)
|
|
735
|
+
ClassRegistry['SA99'].instance_method(:silly).should be(
|
|
736
|
+
ClassRegistry['SA99'].instance_method(:foo))
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
it 'observes undefs and sets the corresponding instance method to nil' do
|
|
740
|
+
input = 'class SA100; def foo; end; end; class SA101 < SA100; undef foo, :inspect; end'
|
|
741
|
+
annotate_all(input)
|
|
742
|
+
ClassRegistry['SA100'].instance_method(:foo).should_not be nil
|
|
743
|
+
ClassRegistry['SA100'].instance_method(:inspect).should_not be nil
|
|
744
|
+
ClassRegistry['SA101'].instance_method(:foo).should be nil
|
|
745
|
+
ClassRegistry['SA101'].instance_method(:inspect).should be nil
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
describe 'complete tests' do
|
|
750
|
+
extend AnalysisHelpers
|
|
751
|
+
clean_registry
|
|
752
|
+
# This is the AST that Ripper generates for the parsed code. It is
|
|
753
|
+
# provided here because otherwise the test is inscrutable.
|
|
754
|
+
#
|
|
755
|
+
# sexp =
|
|
756
|
+
# [:program,
|
|
757
|
+
# [[:module,
|
|
758
|
+
# [:const_ref, [:@const, "And", [1, 7]]],
|
|
759
|
+
# [:bodystmt,
|
|
760
|
+
# [[:void_stmt],
|
|
761
|
+
# [:module,
|
|
762
|
+
# [:const_ref, [:@const, "Or", [2, 9]]],
|
|
763
|
+
# [:bodystmt,
|
|
764
|
+
# [[:void_stmt],
|
|
765
|
+
# [:module,
|
|
766
|
+
# [:const_ref, [:@const, "Is", [3, 11]]],
|
|
767
|
+
# [:bodystmt,
|
|
768
|
+
# [[:void_stmt],
|
|
769
|
+
# [:module,
|
|
770
|
+
# [:const_ref, [:@const, "Ten", [4, 13]]],
|
|
771
|
+
# [:bodystmt,
|
|
772
|
+
# [[:void_stmt],
|
|
773
|
+
# [:module,
|
|
774
|
+
# [:const_ref, [:@const, "Seven", [5, 15]]],
|
|
775
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
776
|
+
# nil, nil, nil]]],
|
|
777
|
+
# nil, nil, nil]]],
|
|
778
|
+
# nil, nil, nil]]],
|
|
779
|
+
# nil, nil, nil]],
|
|
780
|
+
# [:module,
|
|
781
|
+
# [:const_ref, [:@const, "And", [11, 7]]],
|
|
782
|
+
# [:bodystmt,
|
|
783
|
+
# [[:void_stmt],
|
|
784
|
+
# [:class,
|
|
785
|
+
# [:const_path_ref,
|
|
786
|
+
# [:var_ref, [:@const, "Or", [12, 8]]],
|
|
787
|
+
# [:@const, "Type", [12, 12]]],
|
|
788
|
+
# nil,
|
|
789
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
|
|
790
|
+
# [:class,
|
|
791
|
+
# [:const_path_ref,
|
|
792
|
+
# [:const_path_ref,
|
|
793
|
+
# [:const_path_ref,
|
|
794
|
+
# [:var_ref, [:@const, "Or", [14, 8]]],
|
|
795
|
+
# [:@const, "Is", [14, 12]]],
|
|
796
|
+
# [:@const, "Ten", [14, 16]]],
|
|
797
|
+
# [:@const, "Kind", [14, 21]]],
|
|
798
|
+
# [:const_path_ref,
|
|
799
|
+
# [:const_path_ref,
|
|
800
|
+
# [:top_const_ref, [:@const, "And", [14, 30]]],
|
|
801
|
+
# [:@const, "Or", [14, 35]]],
|
|
802
|
+
# [:@const, "Type", [14, 39]]],
|
|
803
|
+
# [:bodystmt,
|
|
804
|
+
# [[:module,
|
|
805
|
+
# [:const_ref, [:@const, "Silly", [15, 11]]],
|
|
806
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
807
|
+
# nil, nil, nil]],
|
|
808
|
+
# [:module,
|
|
809
|
+
# [:const_path_ref,
|
|
810
|
+
# [:const_path_ref,
|
|
811
|
+
# [:const_path_ref,
|
|
812
|
+
# [:const_path_ref,
|
|
813
|
+
# [:var_ref, [:@const, "Or", [18, 9]]],
|
|
814
|
+
# [:@const, "Is", [18, 13]]],
|
|
815
|
+
# [:@const, "Ten", [18, 17]]],
|
|
816
|
+
# [:@const, "Kind", [18, 22]]],
|
|
817
|
+
# [:@const, "Silly", [18, 28]]],
|
|
818
|
+
# [:bodystmt, [[:void_stmt]], nil, nil, nil]]],
|
|
819
|
+
# nil, nil, nil]]]]
|
|
820
|
+
|
|
821
|
+
it 'handles a monstrous comprehensive module and class nesting example' do
|
|
822
|
+
tree = annotate_all(<<-EOF
|
|
823
|
+
module And
|
|
824
|
+
module Or
|
|
825
|
+
module Is
|
|
826
|
+
module Ten
|
|
827
|
+
module Seven
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
module And
|
|
834
|
+
class Or::Type
|
|
835
|
+
end
|
|
836
|
+
class Or::Is::Ten::Kind < ::And::Or::Type
|
|
837
|
+
module Silly
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
module Or::Is::Ten::Kind::Silly
|
|
841
|
+
end
|
|
842
|
+
end
|
|
843
|
+
EOF
|
|
844
|
+
)
|
|
845
|
+
modules = %w(And And::Or And::Or::Is And::Or::Is::Ten And::Or::Is::Ten::Seven
|
|
846
|
+
And::Or::Is::Ten::Kind::Silly)
|
|
847
|
+
modules.each { |mod| ClassRegistry[mod].should be_a(LaserModule) }
|
|
848
|
+
ClassRegistry['And::Or::Type'].should be_a(LaserClass)
|
|
849
|
+
ClassRegistry['And::Or::Type'].superclass.should be ClassRegistry['Object']
|
|
850
|
+
ClassRegistry['And::Or::Is::Ten::Kind'].should be_a(LaserClass)
|
|
851
|
+
ClassRegistry['And::Or::Is::Ten::Kind'].superclass.should be ClassRegistry['And::Or::Type']
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
describe 'with a real ruby file as input' do
|
|
855
|
+
before do
|
|
856
|
+
@input = %q{
|
|
857
|
+
module Laser
|
|
858
|
+
module Analysis
|
|
859
|
+
module Bindings
|
|
860
|
+
# This class represents a Base in Ruby. It may have a known protocol (type),
|
|
861
|
+
# class, value (if constant!), and a variety of other details.
|
|
862
|
+
class Base
|
|
863
|
+
include Comparable
|
|
864
|
+
|
|
865
|
+
def initialize(name, value)
|
|
866
|
+
@name = name
|
|
867
|
+
@value = :uninitialized
|
|
868
|
+
bind!(value)
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
def name
|
|
872
|
+
@name
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
def name=(other)
|
|
876
|
+
@name = other
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
def value
|
|
880
|
+
@value
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
def bind!(value)
|
|
884
|
+
if respond_to?(:validate_value)
|
|
885
|
+
validate_value(value)
|
|
886
|
+
end
|
|
887
|
+
@value = value
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
def <=>(other)
|
|
891
|
+
self.name <=> other.name
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def scope
|
|
895
|
+
value.scope
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
def protocol
|
|
899
|
+
value.protocol
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
def class_used
|
|
903
|
+
value.klass
|
|
904
|
+
end
|
|
905
|
+
#
|
|
906
|
+
# def to_s
|
|
907
|
+
# inspect
|
|
908
|
+
# end
|
|
909
|
+
#
|
|
910
|
+
# def inspect
|
|
911
|
+
# "#<#{self.class.name.split('::').last}: #{name}>"
|
|
912
|
+
# end
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
class KeywordBinding < Base
|
|
916
|
+
private :bind!
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
# Constants have slightly different properties in their bindings: They shouldn't
|
|
920
|
+
# be rebound. However.... Ruby allows it. It prints a warning when the rebinding
|
|
921
|
+
# happens, but we should be able to detect this statically. Oh, and they can't be
|
|
922
|
+
# bound inside a method. That too is easily detected statically.
|
|
923
|
+
class ConstantBinding < Base
|
|
924
|
+
# Require an additional force parameter to rebind a Constant. That way, the user
|
|
925
|
+
# can configure whether rebinding counts as a warning or an error.
|
|
926
|
+
def bind!(val, force=false)
|
|
927
|
+
if @value != :uninitialized && !force
|
|
928
|
+
raise TypeError.new('Cannot rebind a constant binding without const_set')
|
|
929
|
+
end
|
|
930
|
+
super(val)
|
|
931
|
+
end
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
# We may want to track # of assignments/reads from local vars, so we should subclass
|
|
935
|
+
# Base for it.
|
|
936
|
+
class LocalVariableBinding < Base
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# Possible extension ideas:
|
|
940
|
+
# - Initial definition point?
|
|
941
|
+
class GlobalVariableBinding < Base
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
class ArgumentBinding < Base
|
|
945
|
+
attr_reader :kind, :default_value_sexp
|
|
946
|
+
def initialize(name, value, kind, default_value = nil)
|
|
947
|
+
super(name, value)
|
|
948
|
+
@kind = kind
|
|
949
|
+
@default_value_sexp = default_value
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
end
|
|
953
|
+
end
|
|
954
|
+
end
|
|
955
|
+
}
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
it 'correctly resolves many bindings, creates new modules and classes, and defines methods' do
|
|
959
|
+
tree = annotate_all(@input)
|
|
960
|
+
|
|
961
|
+
bindings_mod = 'Laser::Analysis::Bindings'
|
|
962
|
+
ClassRegistry['Laser'].should be_a(LaserModule)
|
|
963
|
+
ClassRegistry['Laser::Analysis'].should be_a(LaserModule)
|
|
964
|
+
ClassRegistry[bindings_mod].should be_a(LaserModule)
|
|
965
|
+
ClassRegistry["#{bindings_mod}::Base"].should be_a(LaserClass)
|
|
966
|
+
ClassRegistry["#{bindings_mod}::KeywordBinding"].should be_a(LaserClass)
|
|
967
|
+
ClassRegistry["#{bindings_mod}::ConstantBinding"].should be_a(LaserClass)
|
|
968
|
+
ClassRegistry["#{bindings_mod}::LocalVariableBinding"].should be_a(LaserClass)
|
|
969
|
+
ClassRegistry["#{bindings_mod}::GlobalVariableBinding"].should be_a(LaserClass)
|
|
970
|
+
ClassRegistry["#{bindings_mod}::ArgumentBinding"].should be_a(LaserClass)
|
|
971
|
+
|
|
972
|
+
ClassRegistry["#{bindings_mod}::Base"].superclass.should be ClassRegistry['Object']
|
|
973
|
+
[ClassRegistry["#{bindings_mod}::KeywordBinding"],
|
|
974
|
+
ClassRegistry["#{bindings_mod}::ConstantBinding"],
|
|
975
|
+
ClassRegistry["#{bindings_mod}::LocalVariableBinding"],
|
|
976
|
+
ClassRegistry["#{bindings_mod}::GlobalVariableBinding"],
|
|
977
|
+
ClassRegistry["#{bindings_mod}::ArgumentBinding"]].each do |subclass|
|
|
978
|
+
subclass.superclass.should be ClassRegistry["#{bindings_mod}::Base"]
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
generic = ClassRegistry["#{bindings_mod}::Base"]
|
|
982
|
+
class_binding = ClassRegistry["#{bindings_mod}::ConstantBinding"]
|
|
983
|
+
kw_binding = ClassRegistry["#{bindings_mod}::KeywordBinding"]
|
|
984
|
+
arg_binding = ClassRegistry["#{bindings_mod}::ArgumentBinding"]
|
|
985
|
+
|
|
986
|
+
arg_binding.ancestors.should == [arg_binding, generic, ClassRegistry['Comparable'],
|
|
987
|
+
ClassRegistry['Object'], ClassRegistry['Kernel'],
|
|
988
|
+
ClassRegistry['BasicObject']]
|
|
989
|
+
|
|
990
|
+
%w(initialize bind! <=> scope protocol class_used to_s inspect).each do |method|
|
|
991
|
+
generic.instance_method(method).should_not be_nil
|
|
992
|
+
generic.visibility_table[method].should == :public
|
|
993
|
+
end
|
|
994
|
+
kw_binding.visibility_table[:bind!].should == :private
|
|
995
|
+
init_method = generic.instance_method(:initialize)
|
|
996
|
+
init_method.arguments.size.should == 2
|
|
997
|
+
init_method.arguments.map(&:name).should == ['name', 'value']
|
|
998
|
+
|
|
999
|
+
arg_binding_method = arg_binding.instance_method(:initialize)
|
|
1000
|
+
arg_binding_method.arguments.size.should == 4
|
|
1001
|
+
arg_binding_method.arguments.map(&:name).should == ['name', 'value', 'kind', 'default_value']
|
|
1002
|
+
|
|
1003
|
+
|
|
1004
|
+
generic.instance_method(:initialize).arity.should == (2..2)
|
|
1005
|
+
generic.instance_method(:bind!).arity.should == (1..1)
|
|
1006
|
+
generic.instance_method(:<=>).arity.should == (1..1)
|
|
1007
|
+
generic.instance_method(:scope).arity.should == (0..0)
|
|
1008
|
+
generic.instance_method(:class_used).arity.should == (0..0)
|
|
1009
|
+
class_binding.instance_method(:bind!).arity.should == (1..2)
|
|
1010
|
+
arg_binding.instance_method(:initialize).arity.should == (3..4)
|
|
1011
|
+
end
|
|
1012
|
+
end
|
|
1013
|
+
end
|