Too much stuff in one commit. Rename blocks now follows language semantics. Removal of more ASTPath consumers
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
9 * The contents of this file are subject to the terms of either the GNU
10 * General Public License Version 2 only ("GPL") or the Common
11 * Development and Distribution License("CDDL") (collectively, the
12 * "License"). You may not use this file except in compliance with the
13 * License. You can obtain a copy of the License at
14 * http://www.netbeans.org/cddl-gplv2.html
15 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16 * specific language governing permissions and limitations under the
17 * License. When distributing the software, include this License Header
18 * Notice in each file and include the License file at
19 * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
20 * particular file as subject to the "Classpath" exception as provided
21 * by Oracle in the GPL Version 2 section of the License file that
22 * accompanied this code. If applicable, add the following below the
23 * License Header, with the fields enclosed by brackets [] replaced by
24 * your own identifying information:
25 * "Portions Copyrighted [year] [name of copyright owner]"
29 * The Original Software is NetBeans. The Initial Developer of the Original
30 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2009 Sun
31 * Microsystems, Inc. All Rights Reserved.
33 * If you wish your version of this file to be governed by only the CDDL
34 * or only the GPL Version 2, indicate your decision by adding
35 * "[Contributor] elects to include this software in this distribution
36 * under the [CDDL or GPL Version 2] license." If you do not indicate a
37 * single choice of license, a recipient has the option to distribute
38 * your version of this file under either the CDDL, the GPL Version 2 or
39 * to extend the choice of license to its licensees as provided above.
40 * However, if you add GPL Version 2 code and therefore, elected the GPL
41 * Version 2 license, then the option applies only if the new code is
42 * made subject to such option by the copyright holder.
44 package org.netbeans.modules.ruby;
47 import org.jrubyparser.ast.MethodDefNode;
48 import org.jrubyparser.ast.Node;
49 import org.netbeans.api.java.classpath.ClassPath;
50 import org.netbeans.editor.BaseDocument;
51 import org.netbeans.modules.csl.spi.ParserResult;
52 import org.netbeans.modules.parsing.api.Source;
53 import org.netbeans.modules.ruby.options.TypeInferenceSettings;
54 import org.openide.filesystems.FileObject;
57 * @todo Test compound assignment: x = File::Stat.new
61 public class RubyTypeAnalyzerTest extends RubyTestBase {
63 public RubyTypeAnalyzerTest(String testName) {
65 RubyIndexer.userSourcesTest = true;
66 TypeInferenceSettings.getDefault().setMethodTypeInference(true);
67 TypeInferenceSettings.getDefault().setRdocTypeInference(true);
71 protected Map<String, ClassPath> createClassPathsForTest() {
72 return rubyTestsClassPath();
75 private RubyTypeInferencer getInferencer(String file, String caretLine, boolean findMethod) throws Exception {
76 FileObject fo = getTestFile("testfiles/" + file);
79 if (caretLine != null) {
80 Source source = Source.create(fo);
81 caretOffset = getCaretOffset(source.createSnapshot().getText().toString(), caretLine);
82 enforceCaretOffset(source, caretOffset);
85 ParserResult parserResult = getParserResult(fo);
86 Node root = AstUtilities.getRoot(parserResult);
88 RubyIndex index = RubyIndex.get(parserResult);
89 Node node = root.getNodeAt(caretOffset);
92 MethodDefNode method = AstUtilities.findMethodAtOffset(root, caretOffset);
93 assertNotNull(method);
97 ContextKnowledge knowledge = new ContextKnowledge(index, root, node, caretOffset, caretOffset, parserResult);
98 return RubyTypeInferencer.create(knowledge);
101 private void assertTypes(final RubyType actualTypes, final String... expectedTypes) {
102 assertTypes(null, actualTypes, expectedTypes);
105 private void assertTypes(final String message, final RubyType actualTypes, final String... expectedTypes) {
106 assertTypes(message, actualTypes, false, expectedTypes);
109 private void assertTypes(final String message, final RubyType actualTypes,
110 final boolean hasUnknownMember, final String... expectedTypes) {
111 RubyType expected = new RubyType(expectedTypes);
112 if (hasUnknownMember) {
113 expected.append(RubyType.unknown());
115 assertTrue(message + ":" +
116 "\n actualTypes: " + actualTypes +
117 "\n expectedTypes: " + expected, actualTypes.equals(expected));
120 private void assertTypes(String relFilePath, String matchingLine,
121 String exprToInfer, String... expectedTypes) throws Exception {
122 assertTypes(relFilePath, matchingLine, exprToInfer, false, expectedTypes);
124 private void assertTypes(String relFilePath, String matchingLine,
125 String exprToInfer, boolean hasUnknownMember, String... expectedTypes) throws Exception {
126 RubyTypeInferencer instance = getInferencer(relFilePath, matchingLine, false);
127 assertTypes("Types correctly inferred", instance.inferType(exprToInfer), hasUnknownMember, expectedTypes);
130 public void testGetType() throws Exception {
131 RubyTypeInferencer instance = getInferencer("types.rb", " l^oc = {", false);
133 assertTypes(instance.inferType("x"), "Integer");
134 // y is reassigned later in the file - make sure that at this
135 // point in scope we have the right type
136 assertTypes(instance.inferType("y"), "File");
137 assertTypes(instance.inferType("$baz"), "Hash");
138 assertTypes(instance.inferType("@bar"), "Fixnum");
139 assertTypes(instance.inferType("@foo"), "Array");
142 public void testGetType2() throws Exception {
143 RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
145 // Y is assigned different types - make sure that at this position, it's a number
146 assertTypes(instance.inferType("y"), "Fixnum");
147 // Lots of reassignments - track types through vars, statics, fields, classvars
148 assertTypes(instance.inferType("loc"), "Hash");
149 assertTypes(instance.inferType("$glob"), "Hash");
150 assertTypes(instance.inferType("@field"), "Hash");
151 assertTypes(instance.inferType("@@clsvar"), "Hash");
152 assertTypes(instance.inferType("loc2"), "Hash");
155 public void testMultipleAssigments() throws Exception {
156 RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
157 assertTypes(instance.inferType("q"), "Fixnum");
158 assertTypes(instance.inferType("w"), "Fixnum");
159 assertTypes(instance.inferType("e"), "String");
160 assertTypes(instance.inferType("@r"), "String");
161 assertTypes(instance.inferType("@t"), "Fixnum");
164 public void testNestedMultipleAssigments() throws Exception {
165 RubyTypeInferencer instance = getInferencer("types.rb", " # d^one", false);
166 assertTypes(instance.inferType("u"), "String");
167 assertTypes(instance.inferType("i"), "Fixnum");
168 assertTypes(instance.inferType("o"), "Float");
169 assertTypes(instance.inferType("p"), "File");
172 public void testTypeAssertions() throws Exception {
173 RubyTypeInferencer instance = getInferencer("types.rb", " l^oc = {", true);
174 assertTypes(instance.inferType("param1"), "String");
175 assertTypes(instance.inferType("param2"), "Hash");
178 public void testBegin() throws Exception {
179 RubyTypeInferencer instance = getInferencer("types2.rb", " @f^iles = ARGV.dup", false);
180 assertTypes(instance.inferType("go"), "GetoptLong");
183 public void testRailsController() throws Exception {
184 assertTypes("type_controller.rb", "^end", "request", "ActionController::CgiRequest");
185 RubyTypeInferencer instance = getInferencer("type_controller.rb", "^end", false);
186 assertTypes(instance.inferType("request"), "ActionController::CgiRequest");
189 // This test doesn't work; the behavior works in the IDE but the
190 // Lucene index isn't returning local symbols in the testing framework yet
191 // public void testComplex1() throws Exception {
192 // RubyTypeInferencer instance = getAnalyzer("types3.rb", "^caret", false);
193 // assertEquals("Product", instance.getType("@product"));
196 // public void testComplex2() throws Exception {
197 // RubyTypeInferencer instance = getAnalyzer("types3.rb", "^caret", true);
198 // assertEquals("ActiveRecord::ConnectionAdapters::TableDefinition", instance.getType("t"));
201 //public void testComplex3() throws Exception {
203 // assertFalse("Check that I do closures for each, collect, map, etc.", true);
204 // // also check to_s
206 // TODO: Make sure I can handle compound expressions like this one:
207 // Product.find(params[:id]).destroy
208 public void testMigrationType() throws Exception {
209 RubyTypeInferencer instance = getInferencer("migrate/20080726182725_create_posts.rb", " t.^time", true);
210 assertTypes(instance.inferType("t"), "ActiveRecord::ConnectionAdapters::TableDefinition");
213 public void testIfType() throws Exception {
214 assertTypes("if_type.rb", "p va^r.in", "var", "String", "NilClass");
217 public void testIfElseType() throws Exception {
218 assertTypes("if_else_type.rb", "p va^r.in", "var", "String", "Array");
221 public void testIfElseType2() throws Exception {
222 assertTypes("if_else_type_2.rb", "p va^r.in", "var", "String", "NilClass");
225 public void testIfElseWithInBlockReassignmentType() throws Exception {
226 assertTypes("if_else_with_block_reassignment_type.rb", "p va^r.in", "var", "Hash", "Array");
229 public void testIfElseIfElseType() throws Exception {
230 assertTypes("if_elsif_else_type.rb", "p va^r.in", "var", "String", "Array", "Hash");
233 public void testUnlessType() throws Exception {
234 assertTypes("unless_type.rb", "var.i^", "var", "Array", "Hash");
237 public void testForType() throws Exception {
238 assertTypes("for_type.rb", "tmp^ = i", "i", false, "Fixnum");
239 assertTypes("for_type.rb", "s^ = j", "j", false, "String");
242 public void testEqualsType() throws Exception {
243 assertTypes("equals_type.rb", "var.t^", "var", "TrueClass", "FalseClass");
244 assertTypes("equals_type.rb", "var2.t^", "var2", "TrueClass", "FalseClass");
247 public void testIfWithFailingInferenceInBranchType() throws Exception {
248 assertTypes("if_with_failing_inference_in_branch_type.rb", "var.to_^", "var", true, "NilClass");
251 // TODO inference is still not able to do the below
252 public void FIXME_testIfElseNestedSimpleType() throws Exception {
253 assertTypes("if_else_nested_simple_type.rb", "var.^ifcond1b", "var", "Float");
254 assertTypes("if_else_nested_simple_type.rb", "var.^aa", "var", "NilClass", "Float");
257 // TODO inference is still not able to do the below
258 public void FIXME_testIfElseNestedType() throws Exception {
259 assertTypes("if_else_nested_type.rb", "va^r.ifcond2", "var", "Hash");
260 // XXX more, see the if_else_nested_type.rb
263 public void testConstant() throws Exception {
264 assertTypes("constants.rb", "Colors::RED.byte^", "Colors::RED", "String");
265 assertTypes("constants.rb", "puts b.down^case", "b", "String");
266 // TODO fix and uncomment when reindexed
267 // assertTypes("indexed constants type inference", "constants.rb", "REXML::COPY^RIGHT", "COPYRIGHT", "String");
270 public void testMethodType() throws Exception {
271 assertTypes("method_type_inference.rb", "puts n^um.abs", "num", "Fixnum");
272 assertTypes("method_type_inference.rb", "puts cons^t", "const", "Fixnum");
275 // public void testCoreMethodType() throws Exception {
276 // assertTypes("core_methods.rb", "ance^stors.delete(String)", "ancestors", "Array");
277 // assertTypes("core_methods.rb", "puts has_^one.t", "has_one", "TrueClass", "FalseClass");
278 // assertTypes("core_methods.rb", "huh = a.eq^l?(123)", "a", "Fixnum", "Numeric");
281 public void testMethodsChaining() throws Exception {
282 assertTypes("methods_chaining.rb", "puts gree^ting", "greeting", "String");