void DeclarationBuilder::visitForStatement(Ast *node) { Node *aux = node->tree; node->tree = node->tree->cond; ExpressionVisitor ev(currentContext(), m_editor); ev.visitNode(node); AbstractType::Ptr type = ev.lastType(); DUChainReadLocker rlock; if (type) { ClassType::Ptr ctype = type.cast<ClassType>(); if (ctype && ctype->contentType()) { type = ctype->contentType().abstractType(); } else { type = getBuiltinsType(QStringLiteral("Object"), currentContext()); } } else { type = getBuiltinsType(QStringLiteral("Object"), currentContext()); } node->tree = aux->r; for (Node *n = node->tree; n != nullptr; n = n->next) { node->tree = n; QualifiedIdentifier id = getIdentifier(node); rlock.unlock(); declareVariable(id, type, node); rlock.lock(); } node->tree = aux; }
void DeclarationBuilder::visitBlockVariables(Ast *node) { DUChainReadLocker rlock; MethodDeclaration *last = dynamic_cast<MethodDeclaration *>(m_lastMethodCall); Node *n = node->tree; if (!n) { return; } uint max = 0, i = 0; const YieldType *yieldList = nullptr; if (last) { yieldList = last->yieldTypes(); max = last->yieldTypesSize(); } AbstractType::Ptr type; for (Node *aux = n; aux != nullptr; aux = aux->next, i++) { node->tree = aux; if (yieldList && i < max) { type = yieldList[i].type.abstractType(); } else { type = getBuiltinsType(QStringLiteral("Object"), currentContext()); } rlock.unlock(); declareVariable(getIdentifier(node), type, node, DUContext::DontSearchInParent); rlock.lock(); } }
void AdaptSignatureAction::execute() { DUChainReadLocker lock; IndexedString url = m_otherSideTopContext->url(); lock.unlock(); m_otherSideTopContext = DUChain::self()->waitForUpdate(url, TopDUContext::AllDeclarationsContextsAndUses); if (!m_otherSideTopContext) { clangDebug() << "failed to update" << url.str(); return; } lock.lock(); Declaration* otherSide = m_otherSideId.getDeclaration(m_otherSideTopContext.data()); if (!otherSide) { clangDebug() << "could not find definition"; return; } DUContext* functionContext = DUChainUtils::getFunctionContext(otherSide); if (!functionContext) { clangDebug() << "no function context"; return; } if (!functionContext || functionContext->type() != DUContext::Function) { clangDebug() << "no correct function context"; return; } DocumentChangeSet changes; KTextEditor::Range parameterRange = ClangIntegration::DUChainUtils::functionSignatureRange(otherSide); QString newText = CodegenHelper::makeSignatureString(otherSide, m_newSignature, !m_editingDefinition); if (!m_editingDefinition) { // append a newline after the method signature in case the method definition follows newText += QLatin1Char('\n'); } DocumentChange changeParameters(functionContext->url(), parameterRange, QString(), newText); changeParameters.m_ignoreOldText = true; changes.addChange(changeParameters); changes.setReplacementPolicy(DocumentChangeSet::WarnOnFailedChange); DocumentChangeSet::ChangeResult result = changes.applyAllChanges(); if (!result) { KMessageBox::error(0, i18n("Failed to apply changes: %1", result.m_failureReason)); } emit executed(this); foreach(RenameAction * renAct, m_renameActions) { renAct->execute(); } }
AbstractType::Ptr CodeCompletionContext::getExpressionType(const QString &token) { AbstractType::Ptr res; QString expr = m_text.left(m_text.lastIndexOf(token)); EditorIntegrator e; ExpressionVisitor ev(m_duContext.data(), &e); DUChainReadLocker lock; Parser parser(IndexedString(), expr.toUtf8()); Ast *ast = parser.parse(); if (!ast || !ast->tree) { return AbstractType::Ptr(nullptr); } lock.unlock(); ev.visitCode(ast); res = ev.lastType(); lock.lock(); return res; }
void TestCppCodegen::testSimplifiedUpdating() { { InsertIntoDUChain code1("duchaintest_1.h", "template <typename T> struct test{};" "template <> struct test<int> {};"); code1.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; //No specializations are handled in simplified parsing Cpp::TemplateDeclaration *specClassDecl = dynamic_cast<Cpp::TemplateDeclaration*>(code1->localDeclarations()[1]); QVERIFY(!specClassDecl->specializedFrom().data()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member2; struct Member1; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_1.h>\n struct C : public A { Member1 m1; Member2 m2; };"); qWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template<class T> struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_2.h>\n #include <duchaintest_1.h>\n struct C : public B<A> { Member1 m1; Member2 m2; };"); qWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); QCOMPARE(code2->localDeclarations().size(), 1); ClassDeclaration* BClass = dynamic_cast<ClassDeclaration*>(code2->localDeclarations()[0]); QVERIFY(BClass); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); DUContext* BAContext = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); QVERIFY(BAContext); QVERIFY(!BAContext->inSymbolTable()); //2 contexts are imported: The template-context and the parent-class context QCOMPARE(BAContext->importedParentContexts().size(), 2); QCOMPARE(BAContext->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(BAContext->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); lock.unlock(); qWarning() << "********************* Parsing step 2"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QCOMPARE(code1->childContexts().size(), 1); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); //BClass should have been updated, not deleted QVERIFY(BClass == dynamic_cast<ClassDeclaration*>(code2->localDeclarations()[0])); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); //The template-instantiation context "B<A>" should have been deleted DUContext* BAContext2 = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); // qDebug() << "BAContexts" << BAContext << BAContext2; // QVERIFY(BAContext != BAContext2); QCOMPARE(BAContext2->importedParentContexts().size(), 2); QCOMPARE(BAContext2->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); classDecl = dynamic_cast<ClassDeclaration*>(BAContext2->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template<class T> struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_2.h>\n #include <duchaintest_1.h>\n typedef B<A> Parent; struct C : public Parent { Member1 m1; Member2 m2; };"); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); lock.unlock(); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); } { QString text = "class C { class D d; };"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; dumpAST(code); //The forward-declaration of 'D' is forwarded into the top-context QCOMPARE(code->localDeclarations().size(), 2); Declaration* classDecl = code->localDeclarations()[0]; QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); lock.unlock(); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdate, true); lock.lock(); QCOMPARE(code->localDeclarations().size(), 2); //Verify that an update has happened, rather than recreating everything QCOMPARE(code->localDeclarations()[0], classDecl); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); } { QString text = "class C {int test(); int mem; }; void test(int a); int i;"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; dumpAST(code); QCOMPARE(code->localDeclarations().size(), 3); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(!code->childContexts()[0]->localDeclarations()[0]->abstractType()); QVERIFY(!code->childContexts()[0]->localDeclarations()[1]->abstractType()); //In simplified parsing mode, the type should not have been built QVERIFY(!code->localDeclarations()[0]->abstractType()); QVERIFY(code->localDeclarations()[0]->kind() == Declaration::Type); QVERIFY(!code->localDeclarations()[1]->abstractType()); QVERIFY(code->localDeclarations()[1]->kind() == Declaration::Instance); QVERIFY(!code->localDeclarations()[2]->abstractType()); QVERIFY(code->localDeclarations()[2]->kind() == Declaration::Instance); { uint count; const KDevelop::CodeModelItem* items; KDevelop::CodeModel::self().items(code->url(), count, items); for(uint a = 0; a < count; ++a) { if(items[a].id == code->localDeclarations()[0]->qualifiedIdentifier()) { QVERIFY(items[a].kind & KDevelop::CodeModelItem::Class); } } } } { InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{}; \n#endif"); InsertIntoDUChain codeB("B.h", "#include <A.h>\n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); lock.unlock(); //Update with more features codeB.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive, true); lock.lock(); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); } { ///Test whether "empty" files work InsertIntoDUChain codeA("Q.h", ""); InsertIntoDUChain codeB("B.h", "#include <Q.h>\n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; QCOMPARE(codeB->importedParentContexts().size(), 1); QVERIFY(codeA->localDeclarations().isEmpty()); QVERIFY(!codeA->parsingEnvironmentFile()->isProxyContext()); } { ///Test the 'ignoring' of header-guards InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{};\n #ifdef HONK\n class Honk {};\n #endif\n \n#endif \n"); InsertIntoDUChain codeB("B.h", "#define A_H\n \n #include <A.h>\n class B{};"); QVERIFY(!codeA.tryGet()); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); lock.unlock(); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::ForceUpdateRecursive | TopDUContext::Recursive); lock.lock(); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); } }
void TestCppCodegen::testUpdateIndices() { /// @todo Extend this test to make sure t hat all kinds of declarations retain their indices when they are updated { InsertIntoDUChain code1("duchaintest_1.h", "class QW{}; struct A { struct Member2; struct Member1; }; class Oq{};"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_1.h>\n struct C : public A { Member1 m1; Member2 m2; A test(int arg) { int v1; \n{}\n { int v2, *v3; }} int test(); };"); qWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; IndexedDeclaration CDecl = code3.getDeclaration("C"); QVERIFY(CDecl.isValid()); IndexedDeclaration ADecl = code3.getDeclaration("A"); QVERIFY(ADecl.isValid()); IndexedDeclaration C_m1 = code3.getDeclaration("C::m1"); QVERIFY(C_m1.isValid()); IndexedDeclaration C_m2 = code3.getDeclaration("C::m2"); QVERIFY(C_m2.isValid()); QVERIFY(CDecl.declaration()->internalContext()); QCOMPARE(CDecl.declaration()->internalContext()->localDeclarations().size(), 4); IndexedDeclaration C_test = CDecl.declaration()->internalContext()->localDeclarations()[2]; QVERIFY(C_test.isValid()); DUContext* testCtx = C_test.data()->internalContext(); QVERIFY(testCtx); QCOMPARE(testCtx->localDeclarations().size(), 1); IndexedDeclaration C_test_v1 = testCtx->localDeclarations()[0]; QCOMPARE(testCtx->childContexts().size(), 2); DUContext* child = testCtx->childContexts()[1]; QCOMPARE(child->localDeclarations().size(), 2); IndexedDeclaration C_test_v2 = child->localDeclarations()[0]; IndexedDeclaration C_test_v3 = child->localDeclarations()[1]; QCOMPARE(C_test_v1.declaration()->identifier(), Identifier("v1")); QCOMPARE(C_test_v2.declaration()->identifier(), Identifier("v2")); QCOMPARE(C_test_v3.declaration()->identifier(), Identifier("v3")); QCOMPARE(C_m1.declaration()->identifier(), Identifier("m1")); QCOMPARE(C_m2.declaration()->identifier(), Identifier("m2")); QCOMPARE(C_test.declaration()->identifier(), Identifier("test")); QCOMPARE(CDecl.declaration()->identifier(), Identifier("C")); QCOMPARE(ADecl.declaration()->identifier(), Identifier("A")); lock.unlock(); code1.m_insertedCode.setText("struct A { struct Member2; struct Member1; };"); code3.m_insertedCode.setText("#include <duchaintest_1.h>\n class Q{}; struct C : public A { Member2 m2; int c; A test(int arg) { int w1; int v1;\n\n { int *v3; }} int test(); };"); code3.parse(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QVERIFY(ADecl.declaration()); QCOMPARE(ADecl.declaration()->identifier(), Identifier("A")); QVERIFY(CDecl.declaration()); QCOMPARE(CDecl.declaration()->identifier(), Identifier("C")); QVERIFY(!C_m1.declaration()); QVERIFY(C_m2.declaration()); QCOMPARE(C_m2.declaration()->identifier(), Identifier("m2")); QVERIFY(C_test.declaration()); QCOMPARE(C_test.declaration()->identifier(), Identifier("test")); QVERIFY(C_test_v1.declaration()); QCOMPARE(C_test_v1.declaration()->identifier(), Identifier("v1")); QVERIFY(!C_test_v2.declaration()); QVERIFY(C_test_v3.declaration()); QCOMPARE(C_test_v3.declaration()->identifier(), Identifier("v3")); } }
void DeclarationBuilder::visitAssignmentStatement(Ast *node) { QList<AbstractType::Ptr> values; QList<DeclarationPointer> declarations; QList<bool> alias; DUChainReadLocker lock; /* First of all, fetch the types and declaration on the right side */ Ast *aux = new Ast(node->tree->r, node->context); for (Node *n = aux->tree; n != nullptr; n = n->next) { ExpressionVisitor v(currentContext(), m_editor); aux->tree = n; lock.unlock(); // TODO: improve this DeclarationBuilderBase::visitNode(aux); v.visitNode(aux); lock.lock(); values << v.lastType(); alias << v.lastAlias(); declarations << v.lastDeclaration(); } lock.unlock(); /* * Check if we can unpack. If it's possible, do it and get out! We can * unpack if the following conditions are satisfied: * - More than 1 expressions on the left side. * - Just one expression on the right side, which has Array as its type. */ int rsize = values.length(); if (rsize == 1) { int rest = nodeListSize(node->tree->l); ClassType::Ptr ct = values.first().cast<ClassType>(); if (rest > 1 && ct && ct->contentType()) { lock.lock(); QualifiedIdentifier qi = ct.data()->declaration(topContext())->qualifiedIdentifier(); lock.unlock(); if (qi == QualifiedIdentifier("Array")) { for (Node *n = node->tree->l; n != nullptr; n = n->next) { aux->tree = n; QualifiedIdentifier id = getIdentifier(aux); declareVariable(id, ct->contentType().abstractType(), aux); } delete aux; return; } } } /* * We cannot unpack, so iterate over the left side expressions * and assign types. */ int i = 0; AbstractType::Ptr type; for (Node *n = node->tree->l; n != nullptr; n = n->next, i++) { if (n->kind == token_method_call) continue; aux->tree = n; if (has_star(n)) { int rest = nodeListSize(n) - 1; int pack = rsize - i - rest; ClassType::Ptr newType = getBuiltinsType(QStringLiteral("Array"), currentContext()).cast<ClassType>(); DUChainWriteLocker wlock; for (int j = pack; j > 0; j--, i++) { newType->addContentType(values.at(i)); } wlock.unlock(); i--; if (!is_just_a_star(n)) { QualifiedIdentifier id = getIdentifier(aux); declareVariable(id, newType.cast<AbstractType>(), aux); } } else if (i < rsize) { if (alias.at(i)) { DUChainWriteLocker wlock; RangeInRevision range = getNameRange(aux); QualifiedIdentifier id = getIdentifier(aux); AliasDeclaration *d = openDeclaration<AliasDeclaration>(id, range); d->setAliasedDeclaration(declarations.at(i).data()); closeDeclaration(); } else { type = values.at(i); if (!type) { // HACK: provisional fix, should be removed in the future type = getBuiltinsType(QStringLiteral("Object"), currentContext()); } QualifiedIdentifier id = getIdentifier(aux); declareVariable(id, type, aux); } } else { lock.lock(); type = getBuiltinsType(QStringLiteral("NilClass"), currentContext()); lock.unlock(); QualifiedIdentifier id = getIdentifier(aux); declareVariable(id, type, aux); } } delete aux; }