T * DeclarationBuilder::reopenDeclaration(const QualifiedIdentifier &id, const RangeInRevision &range, DUContext *context, DeclarationKind kind) { Declaration *res = nullptr; DUChainReadLocker rlock; QList<Declaration *> decls = context->findDeclarations(id); rlock.unlock(); foreach (Declaration *d, decls) { Declaration *fitting = dynamic_cast<T*>(d); if (fitting) { const bool valid = validReDeclaration(d, id, range, kind); if (!valid) return nullptr; rDebug() << "Reopening the following declaration: " << d->toString(); openDeclarationInternal(d); d->setRange(range); setEncountered(d); // TODO: register the re-opening res = d; break; } else rDebug() << "Do not reopen since it's not in the same top context"; }
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 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; }
bool DUChainItemData::execute( QString& /*filterText*/ ) { DUChainReadLocker lock;; Declaration* decl = m_item.m_item.data(); if(!decl) { return false; } if(m_openDefinition && FunctionDefinition::definition(decl)) { decl = FunctionDefinition::definition(decl); } QUrl url = decl->url().toUrl(); KTextEditor::Cursor cursor = decl->rangeInCurrentRevision().start(); DUContext* internal = decl->internalContext(); if(internal && (internal->type() == DUContext::Other || internal->type() == DUContext::Class)) { //Move into the body if(internal->range().end.line > internal->range().start.line) { cursor = KTextEditor::Cursor(internal->range().start.line+1, 0); //Move into the body } } lock.unlock(); ICore::self()->documentController()->openDocument( url, cursor ); return true; }
bool ParseJob::isUpdateRequired(const IndexedString& languageString) { if (abortRequested()) { return false; } if (minimumFeatures() & TopDUContext::ForceUpdate) { return true; } DUChainReadLocker lock; if (abortRequested()) { return false; } foreach(const ParsingEnvironmentFilePointer &file, DUChain::self()->allEnvironmentFiles(document())) { if (file->language() != languageString) { continue; } if (!file->needsUpdate() && file->featuresSatisfied(minimumFeatures())) { kDebug() << "Already up to date" << document().str(); setDuChain(file->topContext()); lock.unlock(); highlightDUChain(); return false; } break; } return !abortRequested(); }
void TypeCorrection::executeSpecifyTypeAction() { QAction* action = qobject_cast<QAction*>(sender()); if ( ! action ) { qCWarning(KDEV_PYTHON_CODEGEN) << "slot not invoked by triggering a QAction, should not happen"; // :) return; } DUChainReadLocker lock; IndexedDeclaration decl = action->data().value<IndexedDeclaration>(); if ( ! decl.isValid() ) { decl = Helper::declarationUnderCursor(); } if ( ! decl.isValid() ) { qCWarning(KDEV_PYTHON_CODEGEN) << "No declaration found!"; return; } CorrectionFileGenerator::HintType hintType; if ( decl.data()->isFunctionDeclaration() ) { hintType = CorrectionFileGenerator::FunctionReturnHint; } else if ( decl.data()->kind() == Declaration::Instance ) { hintType = CorrectionFileGenerator::LocalVariableHint; } else { qCWarning(KDEV_PYTHON_CODEGEN) << "Correction requested for something that's not a local variable or function."; return; } CorrectionAssistant *dialog = new CorrectionAssistant(decl, hintType); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setWindowTitle("Specify type for " + decl.data()->identifier().toString()); connect(dialog, &QDialog::accepted, this, &TypeCorrection::accepted); m_ui->setupUi(dialog); connect(m_ui->buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); connect(m_ui->buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject); if ( hintType == CorrectionFileGenerator::FunctionReturnHint ) { m_ui->kindLabel->setText(i18n("Function return type")); } else if ( hintType == CorrectionFileGenerator::LocalVariableHint ) { m_ui->kindLabel->setText(i18n("Local variable")); } m_ui->identifierLabel->setText(decl.data()->qualifiedIdentifier().toString()); m_ui->typeText->setFocus(); dialog->resize(560, 180); lock.unlock(); dialog->show(); }
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(); } }
void FunctionDeclarationCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word) { qCDebug(KDEV_PYTHON_CODECOMPLETION) << "FunctionDeclarationCompletionItem executed"; KTextEditor::Document* document = view->document(); auto resolvedDecl = Helper::resolveAliasDeclaration(declaration().data()); DUChainReadLocker lock; auto functionDecl = Helper::functionForCalled(resolvedDecl).declaration; lock.unlock(); if ( ! functionDecl && (! resolvedDecl || ! resolvedDecl->abstractType() || resolvedDecl->abstractType()->whichType() != AbstractType::TypeStructure) ) { qCritical(KDEV_PYTHON_CODECOMPLETION) << "ERROR: could not get declaration data, not executing completion item!"; return; } QString suffix = "()"; KTextEditor::Range checkPrefix(word.start().line(), 0, word.start().line(), word.start().column()); KTextEditor::Range checkSuffix(word.end().line(), word.end().column(), word.end().line(), document->lineLength(word.end().line())); if ( m_doNotCall || document->text(checkSuffix).trimmed().startsWith('(') || document->text(checkPrefix).trimmed().endsWith('@') || (functionDecl && Helper::findDecoratorByName(functionDecl, QLatin1String("property"))) ) { // don't insert brackets if they're already there, // the item is a decorator, or if it's an import item. suffix.clear(); } // place cursor behind bracktes by default int skip = 2; if ( functionDecl ) { bool needsArguments = false; int argumentCount = functionDecl->type<FunctionType>()->arguments().length(); if ( functionDecl->context()->type() == KDevelop::DUContext::Class ) { // it's a member function, so it has the implicit self // TODO static methods needsArguments = argumentCount > 1; } else { // it's a free function needsArguments = argumentCount > 0; } if ( needsArguments ) { // place cursor in brackets if there's parameters skip = 1; } } document->replaceText(word, declaration()->identifier().toString() + suffix); view->setCursorPosition( Cursor(word.end().line(), word.end().column() + skip) ); }
QList< ReferencedTopDUContext > ParseSession::contextForThisPackage(IndexedString package) { QList<ReferencedTopDUContext> contexts; QUrl url = package.toUrl(); QDir path(url.adjusted(QUrl::RemoveFilename).path()); if(path.exists()) { int priority = BackgroundParser::WorstPriority; if(!forExport) priority = -1; //import this package as soon as possible else if(m_priority<=-1) priority = BackgroundParser::WorstPriority-2;//all needed files should be scheduled already else priority = m_priority;//currently parsejob does not get created in this cases to reduce recursion QStringList files = path.entryList(QStringList("*.go"), QDir::Files | QDir::NoSymLinks); bool shouldReparse=false; for(QString filename : files) { filename = path.filePath(filename); QFile file(filename); if(!file.exists()) continue; if(forExport && filename.endsWith("_test.go")) continue; IndexedString url(filename); DUChainReadLocker lock; ReferencedTopDUContext context = DUChain::self()->chainForDocument(url); lock.unlock(); if(context) contexts.append(context); else { if(scheduleForParsing(url, priority, (TopDUContext::Features)(TopDUContext::ForceUpdate | TopDUContext::AllDeclarationsAndContexts))) shouldReparse=true; } } if(shouldReparse) scheduleForParsing(m_document, priority+1, (TopDUContext::Features)(m_features | TopDUContext::ForceUpdate)); } return contexts; }
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; }
/** * Currently priority order works in this way * -1: Direct imports of opened file * 0: opened files * ... * 99... imports of imports of imports.... * 99998: Imports of direct imports(needed to resolve types of some function) * 99999: Reparse of direct imports, after its imports are finished * 100000: reparse of opened file, after all recursive imports * layers higher than 99998 are NOT parsed right now because its too slow */ QList<ReferencedTopDUContext> ParseSession::contextForImport(QString package) { package = package.mid(1, package.length()-2); QStringList files; //try canonical paths first if(m_canonicalImports && m_canonicalImports->contains(package)) { QDir path((*m_canonicalImports)[package]); if(path.exists()) { for(const QString& file : path.entryList(QStringList("*.go"), QDir::Files | QDir::NoSymLinks)) files.append(path.filePath(file)); } } if(files.empty()) { for(const QString& pathname : m_includePaths) { QDir path(pathname); if(path.exists() && path.cd(package)) { for(const QString& file : path.entryList(QStringList("*.go"), QDir::Files | QDir::NoSymLinks)) files.append(path.filePath(file)); break; } } } QList<ReferencedTopDUContext> contexts; bool shouldReparse=false; //reduce priority if it is recursive import //int priority = forExport ? m_priority + 2 : m_priority - 1; int priority = BackgroundParser::WorstPriority; if(!forExport) priority = -1; //parse direct imports as soon as possible else if(m_priority<=-1) priority = BackgroundParser::WorstPriority-2;//imports of direct imports to the stack bottom else priority = m_priority - 2;//currently parsejob does not get created in this cases to reduce recursion for(QString filename : files) { QFile file(filename); if(!file.exists()) continue; //test files are not part of binary package, so we can exclude them //we parse test files only if we open them in KDevelop if(filename.endsWith("_test.go")) continue; IndexedString url(filename); DUChainReadLocker lock; ReferencedTopDUContext context = DUChain::self()->chainForDocument(url); lock.unlock(); if(context) contexts.append(context); else { if(scheduleForParsing(url, priority, (TopDUContext::Features)(TopDUContext::ForceUpdate | TopDUContext::AllDeclarationsAndContexts))) shouldReparse = true; } } if(shouldReparse) //reparse this file after its imports are done scheduleForParsing(m_document, priority+1, (TopDUContext::Features)(m_features | TopDUContext::ForceUpdate)); if(!forExport && m_priority != BackgroundParser::WorstPriority)//always schedule last reparse after all recursive imports are done scheduleForParsing(m_document, BackgroundParser::WorstPriority, (TopDUContext::Features)(m_features | TopDUContext::ForceUpdate)); return contexts; }
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; }