File indexing completed on 2024-04-28 04:35:54

0001 /*
0002  * This file is part of KDevelop
0003  * Copyright (C) 2012-2015 Miquel Sabaté Solà <mikisabate@gmail.com>
0004  *
0005  * This program is free software: you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation, either version 3 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 
0020 // Qt + KDevelop
0021 #include <QtTest/QtTest>
0022 #include <language/duchain/duchain.h>
0023 #include <language/duchain/declaration.h>
0024 #include <language/duchain/types/integraltype.h>
0025 #include <language/duchain/types/structuretype.h>
0026 #include <language/duchain/types/unsuretype.h>
0027 #include <language/duchain/types/functiontype.h>
0028 #include <language/duchain/duchainutils.h>
0029 #include <language/duchain/problem.h>
0030 
0031 // Ruby
0032 #include <duchain/tests/duchain.h>
0033 #include <duchain/helpers.h>
0034 #include <duchain/types/classtype.h>
0035 #include <duchain/declarations/methoddeclaration.h>
0036 #include <duchain/declarations/moduledeclaration.h>
0037 #include <duchain/declarations/variabledeclaration.h>
0038 
0039 QTEST_MAIN(ruby::TestDUChain)
0040 
0041 using namespace KDevelop;
0042 namespace ruby
0043 {
0044 
0045 TestDUChain::TestDUChain()
0046 {
0047     /* There's nothing to do here */
0048 }
0049 
0050 TopDUContext * TestDUChain::parse(const QByteArray &code, const QString &id)
0051 {
0052     const QString &name = "duchain_" + id;
0053     return DUChainTestBase::parse(code, name);
0054 }
0055 
0056 void TestDUChain::testUnsureTypes(TypePtr<UnsureType> type, const QStringList &list)
0057 {
0058     for (uint i = 0; i < type->typesSize(); i++) {
0059         QualifiedIdentifier qi = type->types()[i].type<StructureType>()->qualifiedIdentifier();
0060         qDebug() << qi.toString() << " " << list[i];
0061         QCOMPARE(qi, QualifiedIdentifier(list[i]));
0062     }
0063 }
0064 
0065 void TestDUChain::testProblems(TopDUContext *ctx, const QStringList &list)
0066 {
0067     int i = 0;
0068     QList<ProblemPointer> problems = ctx->problems();
0069     QVERIFY(problems.size() == list.size());
0070     foreach (ProblemPointer pp, problems) {
0071         Problem *p = pp.data();
0072         QCOMPARE(p->description(), list[i]);
0073         i++;
0074     }
0075 }
0076 
0077 //BEGIN: Builtin classes
0078 
0079 void TestDUChain::numeric()
0080 {
0081     QByteArray code("a = 1; b = 1.2; c = 12i; d = 34r");
0082     TopDUContext *top = parse(code, "numeric");
0083     DUChainReleaser releaser(top);
0084     DUChainWriteLocker lock;
0085 
0086     Declaration *dec1 = top->localDeclarations().at(0);
0087     QVERIFY(dec1->type<StructureType>());
0088     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0089 
0090     Declaration *dec2 = top->localDeclarations().at(1);
0091     QVERIFY(dec2->type<StructureType>());
0092     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Float"));
0093 
0094     Declaration *dec3 = top->localDeclarations().at(2);
0095     QVERIFY(dec3->type<StructureType>());
0096     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Complex"));
0097 
0098     Declaration *dec4 = top->localDeclarations().at(3);
0099     QVERIFY(dec4->type<StructureType>());
0100     QCOMPARE(dec4->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Rational"));
0101 }
0102 
0103 void TestDUChain::range()
0104 {
0105     QByteArray code("a = 1..42");
0106     TopDUContext *top = parse(code, "range");
0107     DUChainReleaser releaser(top);
0108     DUChainWriteLocker lock;
0109 
0110     Declaration *dec1 = top->localDeclarations().at(0);
0111     QVERIFY(dec1->type<StructureType>());
0112     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Range"));
0113 }
0114 
0115 void TestDUChain::stringAndRegexp()
0116 {
0117     QByteArray code("a = 'string'; b = //");
0118     TopDUContext *top = parse(code, "stringAndRegexp");
0119     DUChainReleaser releaser(top);
0120     DUChainWriteLocker lock;
0121 
0122     QVERIFY(top->localDeclarations().size() == 2);
0123 
0124     /* a = 'string' */
0125     Declaration *dec1 = top->localDeclarations().at(0);
0126     QVERIFY(dec1->type<StructureType>());
0127     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0128 
0129     /* b = // */
0130     Declaration *dec2 = top->localDeclarations().at(1);
0131     QVERIFY(dec2->type<StructureType>());
0132     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
0133 }
0134 
0135 void TestDUChain::booleanAndNil()
0136 {
0137     QByteArray code("a = true; b = false; c = nil");
0138     TopDUContext *top = parse(code, "booleanAndNil");
0139     DUChainReleaser releaser(top);
0140     DUChainWriteLocker lock;
0141 
0142     QVERIFY(top->localDeclarations().size() == 3);
0143 
0144     /* a = true */
0145     Declaration *dec1 = top->localDeclarations().at(0);
0146     QVERIFY(dec1->type<StructureType>());
0147     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("TrueClass"));
0148 
0149     /* b = false */
0150     Declaration *dec2 = top->localDeclarations().at(1);
0151     QVERIFY(dec2->type<StructureType>());
0152     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("FalseClass"));
0153 
0154     /* c = nil */
0155     Declaration *dec3 = top->localDeclarations().at(2);
0156     QVERIFY(dec3->type<StructureType>());
0157     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0158 }
0159 
0160 void TestDUChain::lineFileEncoding()
0161 {
0162     QByteArray code("a = __LINE__; b = __FILE__; c = __ENCODING__");
0163     TopDUContext *top = parse(code, "lineFileEncoding");
0164     DUChainReleaser releaser(top);
0165     DUChainWriteLocker lock;
0166 
0167     QVERIFY(top->localDeclarations().size() == 3);
0168 
0169     /* a = __LINE__ */
0170     Declaration *dec1 = top->localDeclarations().at(0);
0171     QVERIFY(dec1->type<StructureType>());
0172     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0173 
0174     /* b = __FILE__ */
0175     Declaration *dec2 = top->localDeclarations().at(1);
0176     QVERIFY(dec2->type<StructureType>());
0177     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0178 
0179     /* c = __ENCODING__ */
0180     Declaration *dec3 = top->localDeclarations().at(2);
0181     QVERIFY(dec3->type<StructureType>());
0182     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Encoding"));
0183 }
0184 
0185 void TestDUChain::symbol()
0186 {
0187     QByteArray code("a = :a");
0188     TopDUContext *top = parse(code, "symbol");
0189     DUChainReleaser releaser(top);
0190     DUChainWriteLocker lock;
0191 
0192     Declaration *dec = top->localDeclarations().at(0);
0193     QVERIFY(dec->type<StructureType>());
0194     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Symbol"));
0195 }
0196 
0197 void TestDUChain::lambda()
0198 {
0199     QByteArray code("a = ->(a) { puts a }; b = lambda { |x| return x + 1 }");
0200     TopDUContext *top = parse(code, "lambda");
0201     DUChainReleaser releaser(top);
0202     DUChainWriteLocker lock;
0203 
0204     Declaration *dec = top->localDeclarations().at(0);
0205     QVERIFY(dec->type<StructureType>());
0206     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Proc"));
0207 
0208     dec = top->localDeclarations().at(1);
0209     QVERIFY(dec->type<StructureType>());
0210     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Proc"));
0211 }
0212 
0213 void TestDUChain::self()
0214 {
0215     QByteArray code("module Modul; a = self; class Klass; b = self; end; end; c = self; def foo; self; end");
0216     TopDUContext *top = parse(code, "self");
0217     DUChainReleaser releaser(top);
0218     DUChainWriteLocker lock;
0219 
0220     // a
0221     Declaration *d = top->localDeclarations().first();
0222     Declaration *obj = d->internalContext()->localDeclarations().first();
0223     QCOMPARE(obj->qualifiedIdentifier(), QualifiedIdentifier("Modul::a"));
0224     QCOMPARE(obj->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Modul"));
0225 
0226     // b
0227     obj = d->internalContext()->localDeclarations().last()->internalContext()->localDeclarations().first();
0228     QCOMPARE(obj->qualifiedIdentifier(), QualifiedIdentifier("Modul::Klass::b"));
0229     QCOMPARE(obj->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Modul::Klass"));
0230 
0231     // c
0232     d = top->localDeclarations().at(1);
0233     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("c"));
0234     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0235 
0236     // Return type for foo
0237     d = top->localDeclarations().last();
0238     AbstractType::Ptr rType = d->type<FunctionType>()->returnType();
0239     auto structT = rType.staticCast<StructureType>();
0240     QCOMPARE(structT->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0241 }
0242 
0243 //END: Builtin classes
0244 
0245 //BEGIN: Statements
0246 
0247 void TestDUChain::alias()
0248 {
0249     QByteArray code("def foo; 'a' end; alias asd foo");
0250     TopDUContext *top = parse(code, "alias");
0251     DUChainReleaser releaser(top);
0252     DUChainWriteLocker lock;
0253 
0254     /* def foo; end */
0255     Declaration *dec1 = top->localDeclarations().at(0);
0256     QVERIFY(dec1->isFunctionDeclaration());
0257     QCOMPARE(dec1->qualifiedIdentifier(), QualifiedIdentifier("foo"));
0258 
0259     /* alias asd foo */
0260     Declaration *dec2 = top->localDeclarations().at(1);
0261     QVERIFY(dec2->isFunctionDeclaration());
0262     QCOMPARE(dec2->qualifiedIdentifier(), QualifiedIdentifier("asd"));
0263 
0264     /* The return type of asd is also a String */
0265     AbstractType::Ptr rt = dec2->type<FunctionType>()->returnType();
0266     auto structT = rt.staticCast<StructureType>();
0267     QCOMPARE(structT->qualifiedIdentifier(), QualifiedIdentifier("String"));
0268 }
0269 
0270 void TestDUChain::aliasGlobal1()
0271 {
0272     QByteArray code("$a = 0; alias $b $a");
0273     TopDUContext *top = parse(code, "aliasGlobal1");
0274     DUChainReleaser releaser(top);
0275     DUChainWriteLocker lock;
0276 
0277     QVERIFY(top->localDeclarations().size() == 2);
0278 
0279     Declaration *dec1 = top->localDeclarations().at(0);
0280     Declaration *dec2 = top->localDeclarations().at(1);
0281 
0282     QCOMPARE(dec1->qualifiedIdentifier(), QualifiedIdentifier("$a"));
0283     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0284     QCOMPARE(dec2->qualifiedIdentifier(), QualifiedIdentifier("$b"));
0285     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0286 }
0287 
0288 void TestDUChain::aliasGlobal2()
0289 {
0290     QByteArray code("alias $b $a");
0291     TopDUContext *top = parse(code, "aliasGlobal2");
0292     DUChainReleaser releaser(top);
0293     DUChainWriteLocker lock;
0294 
0295     QVERIFY(top->localDeclarations().size() == 2);
0296 
0297     Declaration *dec1 = top->localDeclarations().at(0);
0298     Declaration *dec2 = top->localDeclarations().at(1);
0299 
0300     QCOMPARE(dec1->qualifiedIdentifier(), QualifiedIdentifier("$b"));
0301     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0302     QCOMPARE(dec2->qualifiedIdentifier(), QualifiedIdentifier("$a"));
0303     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0304 }
0305 
0306 void TestDUChain::yield1()
0307 {
0308     QByteArray code("def foo; yield; end; foo { |a, b| puts a + b }");
0309     TopDUContext *top = parse(code, "yield1");
0310     DUChainReleaser releaser(top);
0311     DUChainWriteLocker lock;
0312 
0313     QVector<Declaration *> decls = top->childContexts().last()->localDeclarations();
0314 
0315     QCOMPARE(decls.size(), 2);
0316     Declaration *dec1 = decls.at(0);
0317     Declaration *dec2 = decls.at(1);
0318 
0319     QCOMPARE(dec1->qualifiedIdentifier(), QualifiedIdentifier("a"));
0320     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0321     QCOMPARE(dec2->qualifiedIdentifier(), QualifiedIdentifier("b"));
0322     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0323 }
0324 
0325 void TestDUChain::yield2()
0326 {
0327     QByteArray code("def foo; yield 1, 2; yield 'a', 'b'; end; foo { |a, b| puts a + b }");
0328     TopDUContext *top = parse(code, "yield2");
0329     DUChainReleaser releaser(top);
0330     DUChainWriteLocker lock;
0331 
0332     QVector<Declaration *> decls = top->childContexts().last()->localDeclarations();
0333     QVERIFY(decls.size() == 2);
0334 
0335     Declaration *dec = decls.at(0);
0336     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier("a"));
0337     auto ut = dec->abstractType().dynamicCast<UnsureType>();
0338     QStringList list;
0339     list << "Fixnum" << "String";
0340     testUnsureTypes(ut, list);
0341 
0342     dec = decls.at(1);
0343     QCOMPARE(dec->qualifiedIdentifier(), QualifiedIdentifier("b"));
0344     ut = dec->abstractType().dynamicCast<UnsureType>();
0345     testUnsureTypes(ut, list);
0346 }
0347 
0348 void TestDUChain::yield3()
0349 {
0350     QByteArray code("foo { |a, b| puts a + b }");
0351     TopDUContext *top = parse(code, "yield3");
0352     DUChainReleaser releaser(top);
0353     DUChainWriteLocker lock;
0354 
0355     QVector<Declaration *> decls = top->childContexts().last()->localDeclarations();
0356 
0357     QCOMPARE(decls.size(), 2);
0358     Declaration *dec1 = decls.at(0);
0359     Declaration *dec2 = decls.at(1);
0360 
0361     QCOMPARE(dec1->qualifiedIdentifier(), QualifiedIdentifier("a"));
0362     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0363     QCOMPARE(dec2->qualifiedIdentifier(), QualifiedIdentifier("b"));
0364     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Object"));
0365 }
0366 
0367 void TestDUChain::ifStatement()
0368 {
0369     QByteArray code("a = if d; 1; elsif b; nil; else; 'asd'; end");
0370     TopDUContext *top = parse(code, "ifStatement");
0371     DUChainReleaser releaser(top);
0372     DUChainWriteLocker lock;
0373 
0374     Declaration *dec = top->localDeclarations().at(0);
0375     auto ut = dec->abstractType().dynamicCast<UnsureType>();
0376     QStringList list;
0377     list << "NilClass" << "String" << "Fixnum";
0378     testUnsureTypes(ut, list);
0379 }
0380 
0381 void TestDUChain::caseStatement()
0382 {
0383     QByteArray code("f = case a; when 1; 2; nil; when 2; 1; else; 'asd'; end");
0384     TopDUContext *top = parse(code, "caseStatement");
0385     DUChainReleaser releaser(top);
0386     DUChainWriteLocker lock;
0387 
0388     Declaration *dec = top->localDeclarations().at(0);
0389     auto ut = dec->abstractType().dynamicCast<UnsureType>();
0390     QStringList list;
0391     list << "NilClass" << "Fixnum" << "String";
0392     testUnsureTypes(ut, list);
0393 }
0394 
0395 void TestDUChain::forStatement()
0396 {
0397     QByteArray code("for i in [1, 'str'] do; end");
0398     TopDUContext *top = parse(code, "forStatement");
0399     DUChainReleaser releaser(top);
0400     DUChainWriteLocker lock;
0401 
0402     QCOMPARE(top->localDeclarations().size(), 1);
0403     Declaration *d = top->localDeclarations().first();
0404     auto ut = d->abstractType().dynamicCast<UnsureType>();
0405     QStringList list;
0406     list << "Fixnum" << "String";
0407     testUnsureTypes(ut, list);
0408 }
0409 
0410 void TestDUChain::hereDoc()
0411 {
0412     // heredoc should be a string and should not be parsed as ruby code
0413     QByteArray code("execute <<EOS\n\
0414 foo (\n\
0415 EOS\n\
0416 ");
0417     parse(code, "hereDoc");
0418 
0419     DOES_NOT_CRASH;
0420 }
0421 
0422 void TestDUChain::exceptions()
0423 {
0424     QByteArray code("begin; 1 / 0; rescue ZeroDivisionError, LoadError => e; end");
0425 
0426     {
0427         TopDUContext *top = parse(code, "exceptions");
0428         DUChainReleaser releaser(top);
0429         DUChainWriteLocker lock;
0430         Declaration *d = top->localDeclarations().first();
0431         QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("e"));
0432         auto unsure = d->abstractType().dynamicCast<UnsureType>();
0433         QStringList list;
0434         list << "ZeroDivisionError" << "LoadError";
0435         testUnsureTypes(unsure, list);
0436     }
0437 
0438     {
0439         code = "begin; 1 / 0; rescue ZeroDivisionError, LoadError; end";
0440         TopDUContext *top = parse(code, "exceptions");
0441         DUChainReleaser releaser(top);
0442         DUChainWriteLocker lock;
0443         DOES_NOT_CRASH;
0444     }
0445 }
0446 
0447 void TestDUChain::method()
0448 {
0449     QByteArray code("a = def foo; 0; end");
0450     TopDUContext *top = parse(code, "method");
0451     DUChainReleaser releaser(top);
0452     DUChainWriteLocker lock;
0453 
0454     Declaration *d = top->localDeclarations().first();
0455     QVERIFY(d);
0456     QCOMPARE(d->abstractType()->toString(), QString("Symbol"));
0457 }
0458 
0459 void TestDUChain::classStatement()
0460 {
0461     QByteArray code("a = class Klass; 0; ''; end");
0462     TopDUContext *top = parse(code, "classStatement");
0463     DUChainReleaser releaser(top);
0464     DUChainWriteLocker lock;
0465 
0466     Declaration *d = top->localDeclarations().first();
0467     QVERIFY(d);
0468     QCOMPARE(d->abstractType()->toString(), QString("String"));
0469 }
0470 
0471 //END: Statements
0472 
0473 //BEGIN: Assignments
0474 
0475 void TestDUChain::simpleUnsure()
0476 {
0477     QByteArray code("a = 1; a = 'string'");
0478     TopDUContext *top = parse(code, "simpleUnsure");
0479     DUChainReleaser releaser(top);
0480     DUChainWriteLocker lock;
0481 
0482     QVERIFY(top->localDeclarations().size() == 1);
0483     Declaration *d = top->localDeclarations().first();
0484     auto unsure = d->abstractType().dynamicCast<UnsureType>();
0485     QStringList list;
0486     list << "Fixnum" << "String";
0487     testUnsureTypes(unsure, list);
0488 }
0489 
0490 void TestDUChain::unsureHash()
0491 {
0492     // this test (when run separately from other tests) crashes
0493     // when storing Hash of unsure (Bar::Foo, String) in the item repository
0494     QByteArray code("class Bar; class Foo; end; x = [ Foo.new, 'test' ]; end");
0495     parse(code, "unsureTypeStorage");
0496 
0497     DOES_NOT_CRASH;
0498 }
0499 
0500 void TestDUChain::multipleAssignment1()
0501 {
0502     QByteArray code("a, b = 1, 'a'");
0503     TopDUContext *top = parse(code, "multipleAssignment1");
0504     DUChainReleaser releaser(top);
0505     DUChainWriteLocker lock;
0506 
0507     QVERIFY(top->localDeclarations().size() == 2);
0508 
0509     /* a */
0510     Declaration *dec1 = top->localDeclarations().at(0);
0511     QVERIFY(dec1->type<StructureType>());
0512     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0513 
0514     /* b */
0515     Declaration *dec2 = top->localDeclarations().at(1);
0516     QVERIFY(dec2->type<StructureType>());
0517     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0518 }
0519 
0520 void TestDUChain::multipleAssignment2()
0521 {
0522     QByteArray code("a, b = 1, [1, 2, 3]");
0523     TopDUContext *top = parse(code, "multipleAssignment2");
0524     DUChainReleaser releaser(top);
0525     DUChainWriteLocker lock;
0526 
0527     QVERIFY(top->localDeclarations().size() == 2);
0528 
0529     // a
0530     Declaration *dec = top->localDeclarations().at(0);
0531     QVERIFY(dec->type<StructureType>());
0532     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0533 
0534     /* b */
0535     dec = top->localDeclarations().at(1);
0536     QCOMPARE(dec->type<ClassType>()->toString(), QString("Array of Fixnum"));
0537 }
0538 
0539 void TestDUChain::multipleAssignmentLeft()
0540 {
0541     QByteArray code("a, b, c = 1, 2");
0542     TopDUContext *top = parse(code, "multipleAssignmentLeft");
0543     DUChainReleaser releaser(top);
0544     DUChainWriteLocker lock;
0545 
0546     QVERIFY(top->localDeclarations().size() == 3);
0547 
0548     /* a */
0549     Declaration *dec1 = top->localDeclarations().at(0);
0550     QVERIFY(dec1->type<StructureType>());
0551     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0552 
0553     /* b */
0554     Declaration *dec2 = top->localDeclarations().at(1);
0555     QVERIFY(dec2->type<StructureType>());
0556     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0557 
0558     /* c */
0559     Declaration *dec3 = top->localDeclarations().at(2);
0560     QVERIFY(dec3->type<StructureType>());
0561     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0562 }
0563 
0564 void TestDUChain::multipleAssignmentRight1()
0565 {
0566     QByteArray code("a = 1, 2, 3");
0567     TopDUContext *top = parse(code, "multipleAssignmentRight1");
0568     DUChainReleaser releaser(top);
0569     DUChainWriteLocker lock;
0570 
0571     QVERIFY(top->localDeclarations().size() == 1);
0572 
0573     Declaration *dec = top->localDeclarations().at(0);
0574     QVERIFY(dec->type<StructureType>());
0575     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0576 }
0577 
0578 void TestDUChain::multipleAssignmentRight2()
0579 {
0580     QByteArray code("a, = 1, 2, 3");
0581     TopDUContext *top = parse(code, "multipleAssignmentRight2");
0582     DUChainReleaser releaser(top);
0583     DUChainWriteLocker lock;
0584 
0585     QVERIFY(top->localDeclarations().size() == 1);
0586 
0587     Declaration *dec = top->localDeclarations().at(0);
0588     QVERIFY(dec->type<StructureType>());
0589     QCOMPARE(dec->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0590 }
0591 
0592 void TestDUChain::multipleAssignmentStar()
0593 {
0594     QByteArray code("b = 0; a, *, c = b, nil, 3, 4, 5, 'asd'");
0595     TopDUContext *top = parse(code, "multipleAssignmentStar");
0596     DUChainReleaser releaser(top);
0597     DUChainWriteLocker lock;
0598 
0599     QVERIFY(top->localDeclarations().size() == 3);
0600 
0601     Declaration *dec1 = top->localDeclarations().at(1);
0602     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0603 
0604     Declaration *dec2 = top->localDeclarations().at(2);
0605     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0606 }
0607 
0608 void TestDUChain::multipleAssignmentNamedStar()
0609 {
0610     QByteArray code("a, *b, c = nil, nil, 3, 4, 5, 'asd'");
0611     TopDUContext *top = parse(code, "multipleAssignmentNamedStar");
0612     DUChainReleaser releaser(top);
0613     DUChainWriteLocker lock;
0614 
0615     QVERIFY(top->localDeclarations().size() == 3);
0616 
0617     Declaration *dec1 = top->localDeclarations().at(0);
0618     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0619 
0620     Declaration *dec2 = top->localDeclarations().at(1);
0621     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
0622     QVERIFY(dec2->type<ClassType>()->contentType());
0623     auto unsure = dec2->type<ClassType>()->contentType().abstractType().dynamicCast<UnsureType>();
0624     QStringList list;
0625     list << "NilClass" << "Fixnum";
0626     testUnsureTypes(unsure, list);
0627 
0628     Declaration *dec3 = top->localDeclarations().at(2);
0629     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0630 }
0631 
0632 void TestDUChain::starAtTheBeginning()
0633 {
0634     QByteArray code("*, i = 1, 2, nil");
0635     TopDUContext *top = parse(code, "starAtTheBeginning");
0636     DUChainReleaser releaser(top);
0637     DUChainWriteLocker lock;
0638 
0639     Declaration *dec1 = top->localDeclarations().at(0);
0640     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
0641 }
0642 
0643 void TestDUChain::starAtTheEnd()
0644 {
0645     QByteArray code("q, * = //, 1, 2");
0646     TopDUContext *top = parse(code, "starAtTheEnd");
0647     DUChainReleaser releaser(top);
0648     DUChainWriteLocker lock;
0649 
0650     Declaration *dec1 = top->localDeclarations().at(0);
0651     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
0652 }
0653 
0654 void TestDUChain::emptyStar()
0655 {
0656     QByteArray code("r, *t, w = 1, 'as'");
0657     TopDUContext *top = parse(code, "emptyStar");
0658     DUChainReleaser releaser(top);
0659     DUChainWriteLocker lock;
0660 
0661     QVERIFY(top->localDeclarations().size() == 3);
0662 
0663     Declaration *dec1 = top->localDeclarations().at(0);
0664     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0665 
0666     Declaration *dec2 = top->localDeclarations().at(1);
0667     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
0668 
0669     Declaration *dec3 = top->localDeclarations().at(2);
0670     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0671 }
0672 
0673 void TestDUChain::unpackArray1()
0674 {
0675     QByteArray code("a, b = [1, 2]");
0676     TopDUContext *top = parse(code, "unpackArray1");
0677     DUChainReleaser releaser(top);
0678     DUChainWriteLocker lock;
0679 
0680     QVERIFY(top->localDeclarations().size() == 2);
0681 
0682     Declaration *dec1 = top->localDeclarations().at(0);
0683     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0684 
0685     Declaration *dec2 = top->localDeclarations().at(1);
0686     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0687 }
0688 
0689 void TestDUChain::unpackArray2()
0690 {
0691     QByteArray code("a = [1, 2, 3]; b, c, d = a");
0692     TopDUContext *top = parse(code, "unpackArray2");
0693     DUChainReleaser releaser(top);
0694     DUChainWriteLocker lock;
0695 
0696     QVERIFY(top->localDeclarations().size() == 4);
0697 
0698     Declaration *dec1 = top->localDeclarations().at(1);
0699     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0700 
0701     Declaration *dec2 = top->localDeclarations().at(2);
0702     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0703 
0704     Declaration *dec3 = top->localDeclarations().at(3);
0705     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0706 }
0707 
0708 void TestDUChain::unpackArray3()
0709 {
0710     QByteArray code("a, b = [1, 2], 1");
0711     TopDUContext *top = parse(code, "unpackArray3");
0712     DUChainReleaser releaser(top);
0713     DUChainWriteLocker lock;
0714 
0715     QVERIFY(top->localDeclarations().size() == 2);
0716 
0717     Declaration *dec1 = top->localDeclarations().at(0);
0718     QCOMPARE(dec1->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
0719 
0720     Declaration *dec2 = top->localDeclarations().at(1);
0721     QCOMPARE(dec2->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0722 }
0723 
0724 void TestDUChain::aliasedAssignment()
0725 {
0726     QByteArray code("a = 1..2; b = 1; c, d = a, b");
0727     TopDUContext *top = parse(code, "aliasedAssignment");
0728     DUChainReleaser releaser(top);
0729     DUChainWriteLocker lock;
0730 
0731     QVERIFY(top->localDeclarations().size() == 4);
0732 
0733     Declaration *dec3 = top->localDeclarations().at(2);
0734     QVERIFY(dec3->type<StructureType>());
0735     QCOMPARE(dec3->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Range"));
0736 
0737     Declaration *dec4 = top->localDeclarations().at(3);
0738     QVERIFY(dec4->type<StructureType>());
0739     QCOMPARE(dec4->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0740 }
0741 
0742 void TestDUChain::withMethodCallAndBlock()
0743 {
0744     QByteArray code("a = Class.new do; def foo; b = 0; end; end.foo(1, 2)");
0745     TopDUContext *top = parse(code, "withMethodCallAndBlock");
0746     DUChainReleaser releaser(top);
0747     DUChainWriteLocker lock;
0748 
0749     DOES_NOT_CRASH;
0750 }
0751 
0752 //END: Assignments
0753 
0754 //BEGIN: ClassType
0755 
0756 void TestDUChain::assignFromArrayItem1()
0757 {
0758     QByteArray code("a = [1, 2, 3]; b = a[0]");
0759     TopDUContext *top = parse(code, "assignFromArrayItem1");
0760     DUChainReleaser releaser(top);
0761     DUChainWriteLocker lock;
0762 
0763     QVERIFY(top->localDeclarations().size() == 2);
0764 
0765     // a
0766     Declaration *d = top->localDeclarations().at(0);
0767     QCOMPARE(d->type<ClassType>()->toString(), QString("Array of Fixnum"));
0768 
0769     // b
0770     d = top->localDeclarations().at(1);
0771     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
0772 }
0773 
0774 void TestDUChain::assignFromArrayItem2()
0775 {
0776     QByteArray code("a = [1, '2']; b = a[0]");
0777     TopDUContext *top = parse(code, "assignFromArrayItem2");
0778     DUChainReleaser releaser(top);
0779     DUChainWriteLocker lock;
0780 
0781     QVERIFY(top->localDeclarations().size() == 2);
0782 
0783     // a
0784     Declaration *d = top->localDeclarations().at(0);
0785     QCOMPARE(d->type<ClassType>()->toString(), QString("Array of unsure (Fixnum, String)"));
0786 
0787     // b
0788     d = top->localDeclarations().at(1);
0789     QStringList list;
0790     list << "Fixnum" << "String";
0791     testUnsureTypes(d->type<UnsureType>(), list);
0792 }
0793 
0794 void TestDUChain::assignFromHashItem()
0795 {
0796     QByteArray code("a = { :a => 'a', b: 1 }; b = a[:a]");
0797     TopDUContext *top = parse(code, "assignFromHashItem");
0798     DUChainReleaser releaser(top);
0799     DUChainWriteLocker lock;
0800 
0801     QVERIFY(top->localDeclarations().size() == 2);
0802 
0803     // a
0804     Declaration *d = top->localDeclarations().at(0);
0805     QCOMPARE(d->type<ClassType>()->toString(), QString("Hash of unsure (String, Fixnum)"));
0806 
0807     // b
0808     d = top->localDeclarations().at(1);
0809     QStringList list;
0810     list << "String" << "Fixnum";
0811     testUnsureTypes(d->type<UnsureType>(), list);
0812 }
0813 
0814 void TestDUChain::assignToArrayItem()
0815 {
0816     QByteArray code("a = [1, nil]; a[1] = 'asd'");
0817     TopDUContext *top = parse(code, "assignToArrayItem");
0818     DUChainReleaser releaser(top);
0819     DUChainWriteLocker lock;
0820 
0821     Declaration *d = top->localDeclarations().at(0);
0822     UnsureType::Ptr cont = d->type<ClassType>()->contentType().type<UnsureType>();
0823     QStringList list;
0824     list << "Fixnum" << "NilClass" << "String";
0825     testUnsureTypes(cont, list);
0826 }
0827 
0828 void TestDUChain::arrayInstanceVariable()
0829 {
0830     QByteArray code("class Klass; def foo; @var = [1, 'str']; @var[0]; end; end");
0831     TopDUContext *top = parse(code, "arrayInstanceVariable");
0832     DUChainReleaser releaser(top);
0833     DUChainWriteLocker lock;
0834 
0835     Declaration *d = top->localDeclarations().first();
0836     d = d->internalContext()->localDeclarations().first();
0837     QVERIFY(d);
0838     FunctionType::Ptr fType = d->type<FunctionType>();
0839     QVERIFY(fType);
0840     auto ut = fType->returnType().dynamicCast<UnsureType>();
0841     QVERIFY(ut);
0842     QCOMPARE(ut->typesSize(), (uint) 2);
0843     QStringList list;
0844     list << "Fixnum" << "String";
0845     testUnsureTypes(ut, list);
0846 }
0847 
0848 //END: ClassType
0849 
0850 //BEGIN: Declarations
0851 
0852 void TestDUChain::checkVariableKind()
0853 {
0854     QByteArray code("$a = 0; @a = 0; @@a = 0; a = 0; A = 0");
0855     TopDUContext *top = parse(code, "checkVariableKind");
0856     DUChainReleaser releaser(top);
0857     DUChainWriteLocker lock;
0858 
0859     VariableDeclaration *obj = dynamic_cast<VariableDeclaration *>(top->localDeclarations().at(0));
0860     QVERIFY(obj->isGlobal());
0861 
0862     obj = dynamic_cast<VariableDeclaration *>(top->localDeclarations().at(1));
0863     QVERIFY(obj->isIvar());
0864 
0865     obj = dynamic_cast<VariableDeclaration *>(top->localDeclarations().at(2));
0866     QVERIFY(obj->isCvar());
0867 
0868     obj = dynamic_cast<VariableDeclaration *>(top->localDeclarations().at(3));
0869     QVERIFY(obj->isNormal());
0870 
0871     obj = dynamic_cast<VariableDeclaration *>(top->localDeclarations().at(4));
0872     QVERIFY(obj->isConstant());
0873 }
0874 
0875 void TestDUChain::instanceClassMethodDeclaration()
0876 {
0877     QByteArray code("class Klass; def foo(a, b); end; def asd; end; ");
0878     code += "def self.selfish; end; def Klass.selfis; end; end";
0879     TopDUContext *top = parse(code, "instanceClassMethodDeclaration");
0880     DUChainReleaser releaser(top);
0881     DUChainWriteLocker lock;
0882 
0883     ModuleDeclaration *mod = dynamic_cast<ModuleDeclaration *>(top->localDeclarations().first());
0884     QVERIFY(mod);
0885 
0886     // Instance methods
0887     QVector<Declaration *> decs = mod->internalContext()->localDeclarations();
0888     QCOMPARE(decs.size(), 2);
0889 
0890     MethodDeclaration *d = dynamic_cast<MethodDeclaration *>(decs.first());
0891     QCOMPARE(d->qualifiedIdentifier().toString(), QString("Klass::foo"));
0892     d = dynamic_cast<MethodDeclaration *>(decs.last());
0893     QCOMPARE(d->qualifiedIdentifier().toString(), QString("Klass::asd"));
0894 
0895     // Class methods
0896     decs = mod->eigenClass()->localDeclarations();
0897     QCOMPARE(decs.size(), 2);
0898 
0899     d = dynamic_cast<MethodDeclaration *>(decs.first());
0900     QCOMPARE(d->qualifiedIdentifier().toString(), QString("Klass::Klass::selfish"));
0901     d = dynamic_cast<MethodDeclaration *>(decs.last());
0902     QCOMPARE(d->qualifiedIdentifier().toString(), QString("Klass::Klass::selfis"));
0903 }
0904 
0905 void TestDUChain::reopenMethodDeclaration()
0906 {
0907     QByteArray code("class Klass; def foo; ''; end; end; def foo; 'str'; end; def foo; 0; end");
0908     TopDUContext *top = parse(code, "reopenMethodDeclaration");
0909     DUChainReleaser releaser(top);
0910     DUChainWriteLocker lock;
0911 
0912     QVector<Declaration *> decls = top->localDeclarations();
0913     QCOMPARE(decls.size(), 2);
0914 
0915     Declaration *d = decls.first()->internalContext()->localDeclarations().first();
0916     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("Klass::foo"));
0917     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(d);
0918     AbstractType::Ptr type = md->type<FunctionType>()->returnType();
0919     QCOMPARE(type->toString(), QString("String"));
0920 
0921     d = decls.last();
0922     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("foo"));
0923     md = dynamic_cast<MethodDeclaration *>(d);
0924     type = md->type<FunctionType>()->returnType();
0925     QCOMPARE(type->toString(), QString("Fixnum"));
0926 }
0927 
0928 void TestDUChain::singletonMethods()
0929 {
0930     QByteArray code("def Hash.foo; end; a = 0; def a.lala; end");
0931     TopDUContext *top = parse(code, "singletonMethods");
0932     DUChainReleaser releaser(top);
0933     DUChainWriteLocker lock;
0934 
0935     // Hash.foo
0936     Declaration *d = getBuiltinDeclaration("Hash#foo", top, top);
0937     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(d);
0938     QVERIFY(md);
0939     QVERIFY(md->isClassMethod());
0940 
0941     // a.lala
0942     d = getBuiltinDeclaration("Fixnum#lala", top, top);
0943     md = dynamic_cast<MethodDeclaration *>(d);
0944     QVERIFY(md);
0945     QVERIFY(!md->isClassMethod());
0946 }
0947 
0948 void TestDUChain::singletonClass1()
0949 {
0950     QByteArray code("a = 0; class << a; def foo; 'string'; end; end");
0951     TopDUContext *top = parse(code, "singletonClass1");
0952     DUChainReleaser releaser(top);
0953     DUChainWriteLocker lock;
0954 
0955     Declaration *d = getBuiltinDeclaration("Fixnum#foo", top, top);
0956     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(d);
0957     QVERIFY(md);
0958     QVERIFY(!md->isClassMethod());
0959 
0960     AbstractType::Ptr type = md->type<FunctionType>()->returnType();
0961     QCOMPARE(type.staticCast<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
0962 }
0963 
0964 void TestDUChain::singletonClass2()
0965 {
0966     QSKIP("It crashes right now since I'm not done with the eigenclass thing");
0967 
0968     QByteArray code("class Klass; class << self; def foo; 'string'; end; end; end");
0969     TopDUContext *top = parse(code, "singletonClass2");
0970     DUChainReleaser releaser(top);
0971     DUChainWriteLocker lock;
0972 
0973     DUContext *ctx = top->localDeclarations().first()->internalContext();
0974     Declaration *d = ctx->findDeclarations(QualifiedIdentifier("foo")).first();
0975     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(d);
0976     QVERIFY(md);
0977     QVERIFY(md->isClassMethod());
0978 
0979     auto type = md->type<FunctionType>()->returnType().staticCast<StructureType>();
0980     QCOMPARE(type->qualifiedIdentifier(), QualifiedIdentifier("String"));
0981 }
0982 
0983 void TestDUChain::singletonClass3()
0984 {
0985     QByteArray code("a = 0; a = 'str'; class << a; end");
0986     TopDUContext *top = parse(code, "singletonClass3");
0987     DUChainReleaser releaser(top);
0988     DUChainWriteLocker lock;
0989 
0990     DOES_NOT_CRASH;
0991 }
0992 
0993 void TestDUChain::singletonMethodVisibility()
0994 {
0995     QByteArray code("class << Foo; private; def foo; end; end");
0996     parse(code, "singletonMethodVisibility");
0997 
0998     DOES_NOT_CRASH;
0999 }
1000 
1001 void TestDUChain::accessPolicyMethodInClass()
1002 {
1003     QByteArray code("class Klass; def foo; end; protected; def asd; end; ");
1004     code += "private; def zxc; end; public; def iop; end; end";
1005     TopDUContext *top = parse(code, "accessPolicyMethodInClass");
1006     DUChainReleaser releaser(top);
1007     DUChainWriteLocker lock;
1008     QVector<Declaration *> decs = top->childContexts().first()->localDeclarations();
1009 
1010     MethodDeclaration *d1 = dynamic_cast<MethodDeclaration *>(decs.first());
1011     QVERIFY(d1->accessPolicy() == Declaration::Public);
1012 
1013     MethodDeclaration *d2 = dynamic_cast<MethodDeclaration *>(decs.at(1));
1014     QVERIFY(d2->accessPolicy() == Declaration::Protected);
1015 
1016     MethodDeclaration *d3 = dynamic_cast<MethodDeclaration *>(decs.at(2));
1017     QVERIFY(d3->accessPolicy() == Declaration::Private);
1018 
1019     MethodDeclaration *d4 = dynamic_cast<MethodDeclaration *>(decs.at(3));
1020     QVERIFY(d4->accessPolicy() == Declaration::Public);
1021 }
1022 
1023 void TestDUChain::accessPolicyMethodInModule()
1024 {
1025     QByteArray code("module Klass; def foo; end; protected; def asd; end; ");
1026     code += "private; def zxc; end; public; def iop; end; end";
1027     TopDUContext *top = parse(code, "accessPolicyMethodInClass");
1028     DUChainReleaser releaser(top);
1029     DUChainWriteLocker lock;
1030     QVector<Declaration *> decs = top->childContexts().first()->localDeclarations();
1031 
1032     MethodDeclaration *d1 = dynamic_cast<MethodDeclaration *>(decs.first());
1033     QVERIFY(d1->accessPolicy() == Declaration::Public);
1034 
1035     MethodDeclaration *d2 = dynamic_cast<MethodDeclaration *>(decs.at(1));
1036     QVERIFY(d2->accessPolicy() == Declaration::Protected);
1037 
1038     MethodDeclaration *d3 = dynamic_cast<MethodDeclaration *>(decs.at(2));
1039     QVERIFY(d3->accessPolicy() == Declaration::Private);
1040 
1041     MethodDeclaration *d4 = dynamic_cast<MethodDeclaration *>(decs.at(3));
1042     QVERIFY(d4->accessPolicy() == Declaration::Public);
1043 }
1044 
1045 void TestDUChain::accessPolicyOnBlock()
1046 {
1047     QByteArray code("class Klass; end; Klass.class_eval { private; def foo; end }");
1048     TopDUContext *top = parse(code, "accessPolicyOnBlock");
1049     DUChainReleaser releaser(top);
1050     DUChainWriteLocker lock;
1051 
1052     DOES_NOT_CRASH;
1053 }
1054 
1055 void TestDUChain::nestedAccessPolicy()
1056 {
1057     QByteArray code("class Outer; class Inner; private; def innerFoo; end; ");
1058     code += "end; def outerFoo; end; end";
1059     TopDUContext *top = parse(code, "nestedAccessPolicy");
1060     DUChainReleaser releaser(top);
1061     DUChainWriteLocker lock;
1062     QVector<Declaration *> decs = top->childContexts().first()->localDeclarations();
1063 
1064     // innerFoo
1065     Declaration *d = decs.first()->internalContext()->localDeclarations().first();
1066     QVERIFY(dynamic_cast<MethodDeclaration *>(d)->accessPolicy() == Declaration::Private);
1067 
1068     // outerFoo
1069     d = decs.last();
1070     QVERIFY(dynamic_cast<MethodDeclaration *>(d)->accessPolicy() == Declaration::Public);
1071 }
1072 
1073 void TestDUChain::checkSubClassing1()
1074 {
1075     QByteArray code("class Base; end; class Final < Base; end");
1076     TopDUContext *top = parse(code, "checkSubClassing1");
1077     DUChainReleaser releaser(top);
1078     DUChainWriteLocker lock;
1079 
1080     // Base
1081     ModuleDeclaration *base = dynamic_cast<ModuleDeclaration *>(top->localDeclarations().first());
1082     QVERIFY(base);
1083     QCOMPARE(base->internalContext()->childContexts().count(), 0);
1084     QCOMPARE(base->internalContext()->importedParentContexts().count(), 0);
1085     QCOMPARE(base->internalContext()->localScopeIdentifier(), QualifiedIdentifier("Base"));
1086 
1087     // Final
1088     ModuleDeclaration *final = dynamic_cast<ModuleDeclaration *>(top->localDeclarations().last());
1089     QVERIFY(final);
1090     QCOMPARE(final->internalContext()->childContexts().count(), 0);
1091     QCOMPARE(final->internalContext()->importedParentContexts().count(), 1);
1092     QCOMPARE(final->internalContext()->localScopeIdentifier(), QualifiedIdentifier("Final"));
1093     QVERIFY(final->baseClass());
1094     QCOMPARE(final->baseClass(), base->indexedType());
1095 }
1096 
1097 void TestDUChain::checkSubClassing2()
1098 {
1099     QByteArray code("module A; class B; end; end; class C < A::B; end");
1100     TopDUContext *top = parse(code, "checkSubClassing2");
1101     DUChainReleaser releaser(top);
1102     DUChainWriteLocker lock;
1103 
1104     // B
1105     Declaration *decl = top->localDeclarations().first(); // A
1106     decl = decl->internalContext()->localDeclarations().first(); // B
1107     QVERIFY(decl);
1108     QCOMPARE(decl->internalContext()->childContexts().count(), 0);
1109     QCOMPARE(decl->internalContext()->importedParentContexts().count(), 0);
1110     QCOMPARE(decl->qualifiedIdentifier(), QualifiedIdentifier("A::B"));
1111 
1112     // C
1113     ModuleDeclaration *cDecl = dynamic_cast<ModuleDeclaration *>(top->localDeclarations().last());
1114     QVERIFY(cDecl);
1115     QCOMPARE(cDecl->internalContext()->childContexts().count(), 0);
1116     QCOMPARE(cDecl->internalContext()->importedParentContexts().count(), 1);
1117     QCOMPARE(cDecl->qualifiedIdentifier(), QualifiedIdentifier("C"));
1118     QVERIFY(cDecl->baseClass());
1119     QCOMPARE(cDecl->baseClass(), decl->indexedType());
1120 }
1121 
1122 void TestDUChain::checkSubClassingErrors()
1123 {
1124     QStringList errors;
1125     errors << "TypeError: wrong argument type (expected Class)";
1126 
1127     {
1128         QByteArray code("module A; end; class B < A; end;");
1129         TopDUContext *top = parse(code, "checkSubClassingErrors");
1130         DUChainReleaser releaser(top);
1131         DUChainWriteLocker lock;
1132         testProblems(top, errors);
1133     }
1134 
1135     {
1136         QByteArray code("A = 0; class B < A; end;");
1137         TopDUContext *top = parse(code, "checkSubClassingErrors");
1138         DUChainReleaser releaser(top);
1139         DUChainWriteLocker lock;
1140         testProblems(top, errors);
1141     }
1142 }
1143 
1144 void TestDUChain::errorOnInvalidRedeclaration1()
1145 {
1146     QByteArray code("class Klass; end; module Module; end; class Kernel; end");
1147     TopDUContext *top = parse(code, "errorOnInvalidRedeclaration1");
1148     DUChainReleaser releaser(top);
1149     DUChainWriteLocker lock;
1150 
1151     QStringList errors;
1152     errors << "TypeError: Module is not a module"
1153             << "TypeError: Kernel is not a class";
1154     testProblems(top, errors);
1155 }
1156 
1157 void TestDUChain::errorOnInvalidRedeclaration2()
1158 {
1159     QByteArray code("module Klass; end; module Mod; class Klass; end; end");
1160     TopDUContext *top = parse(code, "errorOnInvalidRedeclaration2");
1161     DUChainReleaser releaser(top);
1162     DUChainWriteLocker lock;
1163 
1164     /*
1165      * This test just checks that no problems are being raised, since the
1166      * second class Klass is inside the module Mod, so it's not the same
1167      * as the first Klass, which is a module.
1168      */
1169 
1170     QStringList errors;
1171     testProblems(top, errors);
1172 }
1173 
1174 void TestDUChain::instanceVariable()
1175 {
1176     // class Klass
1177     //   @asd = 1
1178     //
1179     //   def foo
1180     //     @asd = 'asd'
1181     //     @asd
1182     //   end
1183     //
1184     //   class Mod
1185     //     def lala
1186     //       @asd = 12i
1187     //       @asd
1188     //     end
1189     //   end
1190     // end
1191     //
1192     // a = Klass.new
1193     // b = a.foo
1194     // c = Klass::Mod.new
1195     // d = c.lala
1196 
1197     QByteArray code("class Klass; @asd = 1; def foo; @asd = 'asd'; @asd; end;");
1198     code += " class Mod; def lala; @asd = 12i; @asd; end; end; end;";
1199     code += " a = Klass.new; b = a.foo; c = Klass::Mod.new; d = c.lala";
1200     TopDUContext *top = parse(code, "instanceVariable1");
1201     DUChainReleaser releaser(top);
1202     DUChainWriteLocker lock;
1203 
1204     QList<Declaration *> ds;
1205     foreach (Declaration *d, top->childContexts().first()->localDeclarations())
1206         if (dynamic_cast<VariableDeclaration *>(d))
1207             ds << d;
1208     QCOMPARE(ds.size(), 1);
1209     QCOMPARE(ds.first()->qualifiedIdentifier(), QualifiedIdentifier("Klass::@asd"));
1210     QVERIFY(ds.first()->isAutoDeclaration());
1211 
1212     // It's an unsure(Fixnum, String, Complex)
1213     auto ut = ds.first()->abstractType().dynamicCast<UnsureType>();
1214     QStringList list;
1215     list << "Fixnum" << "String" << "Complex";
1216     testUnsureTypes(ut, list);
1217 
1218     // b is an unsure(Fixnum, String, Complex)
1219     QVector<Declaration *> decls = top->localDeclarations();
1220     Declaration *d = decls.at(2);
1221     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("b"));
1222     ut = d->abstractType().dynamicCast<UnsureType>();
1223     QVERIFY(ut);
1224     testUnsureTypes(ut, list);
1225 
1226     // d is also an unsure(Fixnum, String, Complex)
1227     QCOMPARE(decls.at(4)->qualifiedIdentifier(), QualifiedIdentifier("d"));
1228     ut = decls.at(4)->abstractType().dynamicCast<UnsureType>();
1229     QVERIFY(ut);
1230     testUnsureTypes(ut, list);
1231 }
1232 
1233 void TestDUChain::classVariable()
1234 {
1235     QByteArray code("class Base; @@lala = 1; end; class Klass < Base; @@lala; end");
1236     TopDUContext *top = parse(code, "classVariable");
1237     DUChainReleaser releaser(top);
1238     DUChainWriteLocker lock;
1239 
1240     QList<Declaration *> ds;
1241     foreach (Declaration *d, top->childContexts().first()->localDeclarations())
1242         if (dynamic_cast<VariableDeclaration *>(d))
1243             ds << d;
1244     QCOMPARE(ds.size(), 1);
1245 
1246     // @@lala = 1
1247     Declaration *decl = ds.first();
1248     QVERIFY(decl->isAutoDeclaration());
1249     QCOMPARE(decl->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1250 
1251     // Check that the @@lala inside Klass is the same as the Base one
1252     QCOMPARE(top->childContexts().last()->localDeclarations().size(), 0);
1253     QVector<DUContext::Import> aux = top->childContexts().last()->importedParentContexts();
1254     ds.clear();
1255     foreach (Declaration *d, aux.first().context(top)->localDeclarations())
1256         if (dynamic_cast<VariableDeclaration *>(d))
1257             ds << d;
1258     QCOMPARE(ds.size(), 1);
1259     decl = ds.first();
1260     QCOMPARE(decl->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1261 }
1262 
1263 void TestDUChain::classModulesScopes()
1264 {
1265     QByteArray code("module Klass; end; module Modul; end; class Modul::Klass;");
1266     code += " ::Klass; module Thing; Klass; ::Klass; end; end;";
1267     TopDUContext *top = parse(code, "classModulesScopes");
1268     DUChainReleaser releaser(top);
1269     DUChainWriteLocker lock;
1270 
1271     QVector<Declaration *> decls = top->localDeclarations();
1272     QCOMPARE(decls.size(), 2);
1273 
1274     QCOMPARE(decls.first()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1275     QCOMPARE(decls.at(1)->qualifiedIdentifier(), QualifiedIdentifier("Modul"));
1276 
1277     decls = decls.at(1)->internalContext()->localDeclarations();
1278     QCOMPARE(decls.size(), 1);
1279 
1280     QCOMPARE(decls.first()->qualifiedIdentifier(), QualifiedIdentifier("Modul::Klass"));
1281 
1282     decls = decls.first()->internalContext()->localDeclarations();
1283     QCOMPARE(decls.size(), 1);
1284 
1285     QCOMPARE(decls.first()->qualifiedIdentifier(), QualifiedIdentifier("Klass::Thing"));
1286 }
1287 
1288 void TestDUChain::globals1()
1289 {
1290     QByteArray code("$asd = 1234; class Klass; a = $asd; $asd = 1; class Sub;");
1291     code += " $asd = 'asd'; end; end";
1292     TopDUContext *top = parse(code, "globals1");
1293     DUChainReleaser releaser(top);
1294     DUChainWriteLocker lock;
1295 
1296     // The goal of this test is to check that there's only 3 declarations:
1297     // $asd, Klass and Sub. All assignments of $asd correspond to the same
1298     // global variable.
1299 
1300     // Only 2 declarations: $asd and Klass.
1301     QVector<Declaration *> decls = top->localDeclarations();
1302     Declaration *asd;
1303     QCOMPARE(decls.size(), 2);
1304     asd = decls.first();
1305     QCOMPARE(asd->qualifiedIdentifier(), QualifiedIdentifier("$asd"));
1306     QCOMPARE(decls.last()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1307 
1308     // Only 2 declarations: a and Sub.
1309     decls = decls.last()->internalContext()->localDeclarations();
1310     QCOMPARE(decls.first()->qualifiedIdentifier(), QualifiedIdentifier("Klass::a"));
1311     QCOMPARE(decls.last()->qualifiedIdentifier(), QualifiedIdentifier("Klass::Sub"));
1312 
1313     // No declarations.
1314     decls = decls.last()->internalContext()->localDeclarations();
1315     QVERIFY(decls.empty());
1316 
1317     // Check that $asd is an unsure(Fixnum, String)
1318     auto ut = asd->abstractType().dynamicCast<UnsureType>();
1319     QStringList list;
1320     list << "Fixnum" << "String";
1321     testUnsureTypes(ut, list);
1322 }
1323 
1324 void TestDUChain::globals2()
1325 {
1326     QByteArray code("class Klass; $asd = 1; end; module Mod; $asd = 'asd'; end");
1327     TopDUContext *top = parse(code, "globals2");
1328     DUChainReleaser releaser(top);
1329     DUChainWriteLocker lock;
1330 
1331     // The point of this test is to check that the $asd global variable is set
1332     // in the top DUContext, so there's just one single $asd declaration.
1333     // This $asd variable is expected to be in the top.
1334 
1335     QVector<Declaration *> decls = top->localDeclarations();
1336     QCOMPARE(decls.size(), 3);
1337     Declaration *d = decls.at(1);
1338     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("$asd"));
1339 
1340     // Check that there's no new declaration inside the Mod module.
1341     decls = decls.last()->internalContext()->localDeclarations();
1342     QVERIFY(decls.empty());
1343 
1344     // Check that $asd is an unsure(Fixnum, String)
1345     auto ut = d->abstractType().dynamicCast<UnsureType>();
1346     QStringList list;
1347     list << "Fixnum" << "String";
1348     testUnsureTypes(ut, list);
1349 }
1350 
1351 //END: Declarations
1352 
1353 //BEGIN: Returning Values
1354 
1355 void TestDUChain::multipleReturns()
1356 {
1357     QByteArray code("def foo(a, b); return nil if a.nil?; return 'a'; end");
1358     TopDUContext *top = parse(code, "multipleReturns");
1359     DUChainReleaser releaser(top);
1360     DUChainWriteLocker lock;
1361 
1362     Declaration *decl = top->localDeclarations().first();
1363     FunctionType::Ptr ft = decl->type<FunctionType>();
1364     auto ut = ft->returnType().dynamicCast<UnsureType>();
1365     QStringList list;
1366     list << "String" << "NilClass";
1367     testUnsureTypes(ut, list);
1368 }
1369 
1370 void TestDUChain::implicitReturn()
1371 {
1372     QByteArray code("class Klass; def item; @list = [1, 'str']; @list[0]; end; end;");
1373     code += "a = Klass.new; b = a.item";
1374     TopDUContext *top = parse(code, "returnFromInstanceMethod");
1375     DUChainReleaser releaser(top);
1376     DUChainWriteLocker lock;
1377 
1378     Declaration *d = top->localDeclarations().last();
1379     UnsureType::Ptr ut = d->type<UnsureType>();
1380     QVERIFY(ut);
1381     QStringList list;
1382     list << "Fixnum" << "String";
1383     testUnsureTypes(ut, list);
1384 }
1385 
1386 void TestDUChain::mixedExplicitAndImplicitReturn()
1387 {
1388     QByteArray code("def foo(a); return nil if a.nil?; 'a'; end");
1389     TopDUContext *top = parse(code, "mixedExplicitAndImplicitReturn");
1390     DUChainReleaser releaser(top);
1391     DUChainWriteLocker lock;
1392 
1393     Declaration *decl = top->localDeclarations().first();
1394     FunctionType::Ptr ft = decl->type<FunctionType>();
1395     auto ut = ft->returnType().dynamicCast<UnsureType>();
1396     QStringList list;
1397     list << "String" << "NilClass";
1398     testUnsureTypes(ut, list);
1399 }
1400 
1401 void TestDUChain::nilReturn()
1402 {
1403     QByteArray code("def foo; end");
1404     TopDUContext *top = parse(code, "nilReturn");
1405     DUChainReleaser releaser(top);
1406     DUChainWriteLocker lock;
1407 
1408     Declaration *decl = top->localDeclarations().first();
1409     FunctionType::Ptr ft = decl->type<FunctionType>();
1410     auto rt = ft->returnType().staticCast<StructureType>();
1411     QCOMPARE(rt->qualifiedIdentifier(), QualifiedIdentifier("NilClass"));
1412 }
1413 
1414 void TestDUChain::instanceClassMethodsReturn()
1415 {
1416     QByteArray code("class Klass; def self.foo; 0; end; def foo; ''; end; end; a = Klass.new; b = a.foo; c = Klass.foo");
1417     TopDUContext *top = parse(code, "instanceClassMethodsReturn");
1418     DUChainReleaser releaser(top);
1419     DUChainWriteLocker lock;
1420 
1421     qDebug() << top->localDeclarations().size();
1422 
1423     Declaration *d = top->localDeclarations().at(2);
1424     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("b"));
1425     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
1426 
1427     d = top->localDeclarations().last();
1428     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("c"));
1429     qDebug() << d->type<StructureType>()->qualifiedIdentifier().toString();
1430     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1431 }
1432 
1433 //END: Returning Values
1434 
1435 //BEGIN: Methods
1436 
1437 void TestDUChain::callingToInstanceMethod()
1438 {
1439     QByteArray code("class Klass; def foo; 1; end; end; obj = Klass.new; a = obj.foo");
1440     TopDUContext *top = parse(code, "callingToInstanceMethod");
1441     DUChainReleaser releaser(top);
1442     DUChainWriteLocker lock;
1443 
1444     Declaration *obj = top->localDeclarations().at(1);
1445     QCOMPARE(obj->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1446 
1447     obj = top->localDeclarations().at(2);
1448     QCOMPARE(obj->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1449 }
1450 
1451 void TestDUChain::chainedCalls1()
1452 {
1453     QByteArray code("module Modul; class Klass; def self.selfish(a, b); ");
1454     code += "'str'; end; end; end; a = Modul::Klass.selfish(1, 2)";
1455     TopDUContext *top = parse(code, "chainedCalls1");
1456     DUChainReleaser releaser(top);
1457     DUChainWriteLocker lock;
1458 
1459     Declaration *d = top->localDeclarations().last();
1460     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("a"));
1461     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
1462 }
1463 
1464 void TestDUChain::chainedCalls2()
1465 {
1466     QByteArray code("class Base; def foo; 'asd'; end; end; class Klass; ");
1467     code += "def foo1; a = Base.new; a.foo; end; end; b = Klass.new; c = b.foo1";
1468     TopDUContext *top = parse(code, "chainedCalls2");
1469     DUChainReleaser releaser(top);
1470     DUChainWriteLocker lock;
1471 
1472     // b
1473     Declaration *d = top->localDeclarations().at(2);
1474     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("b"));
1475     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1476 
1477     // c
1478     d = top->localDeclarations().last();
1479     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("c"));
1480     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
1481 }
1482 
1483 void TestDUChain::chainedCalls3()
1484 {
1485     // TODO
1486     QSKIP("There's still some work to do before getting this test to pass");
1487 
1488     QByteArray code("module A; class B; def foo; //; end; end; a = B.new.foo;");
1489     code += "end; b = A::B.new.foo";
1490     TopDUContext *top = parse(code, "chainedCalls3");
1491     DUChainReleaser releaser(top);
1492     DUChainWriteLocker lock;
1493 
1494     // a
1495     Declaration *d = top->localDeclarations().first();
1496     d = d->internalContext()->localDeclarations().last();
1497     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("A::a"));
1498     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
1499 
1500     // b
1501     d = top->localDeclarations().last();
1502     QCOMPARE(d->qualifiedIdentifier(), QualifiedIdentifier("b"));
1503     QCOMPARE(d->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
1504 }
1505 
1506 void TestDUChain::super()
1507 {
1508     QByteArray code("class Base; def foo; 'string'; end; end; class Klass < ");
1509     code += "Base; def foo; super; end; end";
1510     TopDUContext *top = parse(code, "super");
1511     DUChainReleaser releaser(top);
1512     DUChainWriteLocker lock;
1513 
1514     DUContext *ctx = top->localDeclarations().last()->internalContext();
1515     Declaration *d = ctx->findDeclarations(QualifiedIdentifier("foo")).first();
1516     QVERIFY(d);
1517     auto sType = d->type<FunctionType>()->returnType().dynamicCast<StructureType>();
1518     QVERIFY(sType);
1519     QCOMPARE(sType->qualifiedIdentifier(), QualifiedIdentifier("String"));
1520 }
1521 
1522 // Defines a method with an amazing list of arguments
1523 const QByteArray foo("def foo(a, b = 0, c = 0, *d, e, f, &blk); end; ");
1524 
1525 void TestDUChain::guessArgumentsType1()
1526 {
1527     QByteArray code(foo + "foo 1, 'str', //");
1528     TopDUContext *top = parse(code, "guessArgumentsType1");
1529     DUChainReleaser releaser(top);
1530     DUChainWriteLocker lock;
1531 
1532     Declaration *d = top->localDeclarations().first();
1533     QVector<Declaration *> args = DUChainUtils::argumentContext(d)->localDeclarations();
1534     QCOMPARE(args.size(), 7);
1535 
1536     // a, b and c are just Fixnum
1537     QCOMPARE(args.first()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1538     QCOMPARE(args.at(1)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1539     QCOMPARE(args.at(2)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1540 
1541     // d is an empty array
1542     QCOMPARE(args.at(3)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
1543     QVERIFY(! args.at(3)->type<ClassType>()->contentType());
1544 
1545     // e is a String and f is a Regexp
1546     QCOMPARE(args.at(4)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("String"));
1547     QCOMPARE(args.at(5)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
1548 
1549     // blk is just a Proc
1550     QCOMPARE(args.last()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Proc"));
1551 }
1552 
1553 void TestDUChain::guessArgumentsType2()
1554 {
1555     QByteArray code(foo + "foo 1, 'str', 1.2, //");
1556     TopDUContext *top = parse(code, "guessArgumentsType2");
1557     DUChainReleaser releaser(top);
1558     DUChainWriteLocker lock;
1559 
1560     Declaration *d = top->localDeclarations().first();
1561     QVector<Declaration *> args = DUChainUtils::argumentContext(d)->localDeclarations();
1562     QCOMPARE(args.size(), 7);
1563 
1564     // a is just a Fixnum
1565     QCOMPARE(args.first()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1566 
1567     // b is an Unsure of Fixnum and String
1568     auto unsure = args.at(1)->abstractType().dynamicCast<UnsureType>();
1569     QStringList list;
1570     list << "Fixnum" << "String";
1571     testUnsureTypes(unsure, list);
1572 
1573     // c is just a Fixnum
1574     QCOMPARE(args.at(2)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1575 
1576     // d is an empty array
1577     QCOMPARE(args.at(3)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
1578     QVERIFY(! args.at(3)->type<ClassType>()->contentType());
1579 
1580     // e is a Float and f is a Regexp
1581     QCOMPARE(args.at(4)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Float"));
1582     QCOMPARE(args.at(5)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
1583 
1584     // blk is just a Proc
1585     QCOMPARE(args.last()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Proc"));
1586 }
1587 
1588 void TestDUChain::guessArgumentsType3()
1589 {
1590     QByteArray code(foo + "foo 1, 'str', 1, 2, 'str', 1.2, //");
1591     TopDUContext *top = parse(code, "guessArgumentsType3");
1592     DUChainReleaser releaser(top);
1593     DUChainWriteLocker lock;
1594 
1595     Declaration *d = top->localDeclarations().first();
1596     QVector<Declaration *> args = DUChainUtils::argumentContext(d)->localDeclarations();
1597     QCOMPARE(args.size(), 7);
1598 
1599     // a is just a Fixnum
1600     QCOMPARE(args.first()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1601 
1602     // b is an Unsure of Fixnum and String
1603     auto unsure = args.at(1)->abstractType().dynamicCast<UnsureType>();
1604     QStringList list;
1605     list << "Fixnum" << "String";
1606     testUnsureTypes(unsure, list);
1607 
1608     // c is just a Fixnum
1609     QCOMPARE(args.at(2)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1610 
1611     // d is an empty array
1612     QCOMPARE(args.at(3)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
1613     unsure = args.at(3)->type<ClassType>()->contentType().abstractType().dynamicCast<UnsureType>();
1614     list.clear();
1615     list << "Fixnum" << "String";
1616     testUnsureTypes(unsure, list);
1617 
1618     // e is a Float and f is a Regexp
1619     QCOMPARE(args.at(4)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Float"));
1620     QCOMPARE(args.at(5)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Regexp"));
1621 
1622     // blk is just a Proc
1623     QCOMPARE(args.last()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Proc"));
1624 }
1625 
1626 void TestDUChain::showErrorOnTooFewArguments()
1627 {
1628     QByteArray code("def foo(a, b = 0, c = 0, *d, e, f, &blk); end; foo 1, 2");
1629     TopDUContext *top = parse(code, "showErrorOnTooFewArguments");
1630     DUChainReleaser releaser(top);
1631     DUChainWriteLocker lock;
1632 
1633     QStringList errors;
1634     errors << "wrong number of arguments (2 for 3) (ArgumentError)";
1635     testProblems(top, errors);
1636 }
1637 
1638 void TestDUChain::showErrorOnTooManyArguments()
1639 {
1640     QByteArray code("def foo(a, b); end; foo 1, 2, 3");
1641     TopDUContext *top = parse(code, "showErrorOnTooManyArguments");
1642     DUChainReleaser releaser(top);
1643     DUChainWriteLocker lock;
1644 
1645     QStringList errors;
1646     errors << "wrong number of arguments (3 for 2) (ArgumentError)";
1647     testProblems(top, errors);
1648 }
1649 
1650 void TestDUChain::hashArgument()
1651 {
1652     QByteArray code("def foo(a, *b, c); end; foo 1, 2, 'string', :a => 'a', :b => 1");
1653     TopDUContext *top = parse(code, "hashArgument");
1654     DUChainReleaser releaser(top);
1655     DUChainWriteLocker lock;
1656 
1657     Declaration *d = top->localDeclarations().first();
1658     QVector<Declaration *> args = DUChainUtils::argumentContext(d)->localDeclarations();
1659     QCOMPARE(args.size(), 3);
1660 
1661     // a
1662     QCOMPARE(args.first()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1663 
1664     // b
1665     QCOMPARE(args.at(1)->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Array"));
1666     auto unsure = args.at(1)->type<ClassType>()->contentType().abstractType().dynamicCast<UnsureType>();
1667     QStringList list;
1668     list << "Fixnum" << "String";
1669     testUnsureTypes(unsure, list);
1670 
1671     // c
1672     QCOMPARE(args.last()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Hash"));
1673     unsure = args.last()->type<ClassType>()->contentType().abstractType().dynamicCast<UnsureType>();
1674     list.clear();
1675     list << "String" << "Fixnum";
1676     testUnsureTypes(unsure, list);
1677 }
1678 
1679 void TestDUChain::setUnsureArgument()
1680 {
1681     QByteArray code("def foo(a, b); end; foo 1, 2; foo 'asd', 2");
1682     TopDUContext *top = parse(code, "setUnsureArgument");
1683     DUChainReleaser releaser(top);
1684     DUChainWriteLocker lock;
1685 
1686     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(top->localDeclarations().first());
1687     QVERIFY(md);
1688     QVector<Declaration *> args = DUChainUtils::argumentContext(md)->localDeclarations();
1689     QVERIFY(args.size() == 2);
1690     auto unsure = args.first()->indexedType().abstractType().dynamicCast<UnsureType>();
1691     QStringList list;
1692     list << "Fixnum" << "String";
1693     testUnsureTypes(unsure, list);
1694     QCOMPARE(args.last()->type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Fixnum"));
1695 }
1696 
1697 void TestDUChain::conflictOnSpecialMethods()
1698 {
1699     QByteArray code("module Modul; end; class Klass; extend Modul; ");
1700     code += "def include; end; private = 1; end";
1701     TopDUContext *top = parse(code, "conflictOnSpecialMethods");
1702     DUChainReleaser releaser(top);
1703     DUChainWriteLocker lock;
1704 
1705     QVector<Declaration *> decls = top->localDeclarations();
1706     ModuleDeclaration *modul = dynamic_cast<ModuleDeclaration *>(decls.first());
1707     ModuleDeclaration *klass = dynamic_cast<ModuleDeclaration *>(decls.last());
1708     QVERIFY(modul);
1709     QVERIFY(klass);
1710 
1711     /*
1712      * First check that special methods that have been re-declared are being
1713      * properly handled.
1714      */
1715 
1716     decls = klass->internalContext()->localDeclarations();
1717     QCOMPARE(decls.size(), 2);
1718 
1719     MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(decls.first());
1720     VariableDeclaration *vd = dynamic_cast<VariableDeclaration *>(decls.last());
1721     QVERIFY(md);
1722     QVERIFY(vd);
1723     QCOMPARE(md->qualifiedIdentifier(), QualifiedIdentifier("Klass::include"));
1724     QCOMPARE(vd->qualifiedIdentifier(), QualifiedIdentifier("Klass::private"));
1725 
1726     /*
1727      * Finally, check that since the "extend" method hasn't been touched, it
1728      * should've extended the Klass class as expected.
1729      */
1730 
1731     QCOMPARE(klass->moduleMixinsSize(), 1u);
1732     QVERIFY(!klass->moduleMixins()[0].included);
1733     QCOMPARE(klass->moduleMixins()[0].module.type<StructureType>()->qualifiedIdentifier(), modul->qualifiedIdentifier());
1734 }
1735 
1736 //END: Methods
1737 
1738 //BEGIN: Include & Extend
1739 
1740 void TestDUChain::include1()
1741 {
1742     /*
1743      * The class Klass includes the module AA::BB that has the instance
1744      * method foo.
1745      */
1746     QByteArray code("module AA; module BB; def foo; end; def self.selfish; end;");
1747     code += "end; end; class Klass; include AA::BB; end";
1748     TopDUContext *top = parse(code, "include1");
1749     DUChainReleaser releaser(top);
1750     DUChainWriteLocker lock;
1751 
1752     Declaration *obj = top->findLocalDeclarations(Identifier("Klass")).first();
1753     ModuleDeclaration *md = dynamic_cast<ModuleDeclaration *>(obj);
1754     QVERIFY(md);
1755 
1756     // Check for the moduleMixins list of the class Klass
1757     QCOMPARE(md->internalContext()->localDeclarations(md->topContext()).size(), 1);
1758     QCOMPARE(md->moduleMixinsSize(), 1u);
1759     QVERIFY(md->moduleMixins()[0].included);
1760     QCOMPARE(md->moduleMixins()[0].module.type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("AA::BB"));
1761     QCOMPARE(md->mixersSize(), 0u);
1762 
1763     // Check for the mixers list of the module AA::BB
1764     obj = top->localDeclarations().at(0)->internalContext()->localDeclarations().first();
1765     md = dynamic_cast<ModuleDeclaration *>(obj);
1766     QVERIFY(md);
1767     QCOMPARE(md->moduleMixinsSize(), 0u);
1768     QCOMPARE(md->mixersSize(), 1u);
1769     QCOMPARE(md->mixers()[0].module.type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1770 }
1771 
1772 void TestDUChain::include2()
1773 {
1774     /*
1775      * The module AA::CC includes the module AA::BB, that has the instance
1776      * method foo. Finally, the class Klass includes the module AA::CC.
1777      */
1778     QByteArray code("module AA; module BB; def foo; end; end; module CC; ");
1779     code += "include ::AA::BB; end; end; class Klass; include AA::CC; end";
1780     TopDUContext *top = parse(code, "include2");
1781     DUChainReleaser releaser(top);
1782     DUChainWriteLocker lock;
1783 
1784     Declaration *obj = top->findLocalDeclarations(Identifier("Klass")).first();
1785     ModuleDeclaration *md = dynamic_cast<ModuleDeclaration *>(obj);
1786     QVERIFY(md);
1787     QCOMPARE(md->internalContext()->localDeclarations(md->topContext()).size(), 1);
1788     QCOMPARE(md->moduleMixinsSize(), 1u);
1789     QVERIFY(md->moduleMixins()[0].included);
1790     QCOMPARE(md->moduleMixins()[0].module.type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("AA::CC"));
1791 }
1792 
1793 void TestDUChain::extend()
1794 {
1795     /* Same as include1 but with extend and a class method */
1796     QByteArray code("module AA; module BB; def foo; end; def self.selfish; end;");
1797     code += "end; end; class Klass; extend AA::BB; end";
1798     TopDUContext *top = parse(code, "extend");
1799     DUChainReleaser releaser(top);
1800     DUChainWriteLocker lock;
1801 
1802     // Check for the moduleMixins list of the class Klass
1803     Declaration *obj = top->findLocalDeclarations(Identifier("Klass")).first();
1804     ModuleDeclaration *md = dynamic_cast<ModuleDeclaration *>(obj);
1805     QVERIFY(md);
1806     QCOMPARE(md->internalContext()->localDeclarations(md->topContext()).size(), 1);
1807     QCOMPARE(md->moduleMixinsSize(), 1u);
1808     QVERIFY(!md->moduleMixins()[0].included);
1809     QCOMPARE(md->moduleMixins()[0].module.type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("AA::BB"));
1810 
1811     // Check for the mixers list of the module AA::BB
1812     obj = top->localDeclarations().at(0)->internalContext()->localDeclarations().first();
1813     md = dynamic_cast<ModuleDeclaration *>(obj);
1814     QVERIFY(md);
1815     QCOMPARE(md->moduleMixinsSize(), 0u);
1816     QCOMPARE(md->mixersSize(), 1u);
1817     QCOMPARE(md->mixers()[0].module.type<StructureType>()->qualifiedIdentifier(), QualifiedIdentifier("Klass"));
1818 }
1819 
1820 void TestDUChain::problemOnInvalidMixin()
1821 {
1822     QByteArray code("class Lala; end; class Klass; include Lala; end");
1823     TopDUContext *top = parse(code, "problemOnInvalidMixin");
1824     DUChainReleaser releaser(top);
1825     DUChainWriteLocker lock;
1826 
1827     QStringList list;
1828     list << "TypeError: wrong argument type (expected Module)";
1829     testProblems(top, list);
1830 }
1831 
1832 //END: Include & Extend
1833 
1834 }
1835