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