Example #1
0
///The first definition that belongs to a context that surrounds the current cursor
Declaration* cursorContextDeclaration() {
  KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView();
  if(!view)
    return nullptr;

  KDevelop::DUChainReadLocker lock( DUChain::lock() );

  TopDUContext* ctx = DUChainUtils::standardContextForUrl(view->document()->url());
  if(!ctx)
    return nullptr;

  KTextEditor::Cursor cursor(view->cursorPosition());

  DUContext* subCtx = ctx->findContext(ctx->transformToLocalRevision(cursor));

  while(subCtx && !subCtx->owner())
    subCtx = subCtx->parentContext();

  Declaration* definition = nullptr;

  if(!subCtx || !subCtx->owner())
    definition = DUChainUtils::declarationInLine(cursor, ctx);
  else
    definition = subCtx->owner();

  if(!definition)
    return nullptr;

  return definition;
}
QPair<DUContext*, QualifiedIdentifier> ContextBuilder::findPrefixContext(const QualifiedIdentifier& id, KDevelop::SimpleCursor pos) {
  if(id.count() < 2)
    return qMakePair((DUContext*)0, QualifiedIdentifier());
  
  QualifiedIdentifier prefixId(id);
  prefixId.pop();

  DUContext* import = 0;

  {
    DUChainReadLocker lock(DUChain::lock());

    QualifiedIdentifier currentScopeId = currentContext()->scopeIdentifier(true);

    QList<Declaration*> decls = currentContext()->findDeclarations(prefixId, pos);

    if(!decls.isEmpty()) {
      DUContext* classContext = decls.first()->logicalInternalContext(0);
      if(classContext && classContext->type() == DUContext::Class) {
        import = classContext;
        //Change the prefix-id so it respects namespace-imports
        
        prefixId = classContext->scopeIdentifier(true);
        if(prefixId.count() >= currentScopeId.count() && prefixId.left(currentScopeId.count()) == currentScopeId)
          prefixId = prefixId.mid(currentScopeId.count());
        else
          kDebug() << "resolved bad prefix context. Should start with" << currentScopeId.toString() << "but is" << prefixId.toString();
      }
    }
  }
  
  return qMakePair(import, prefixId);
}
Example #3
0
QList<CompletionTreeItemPointer> CodeCompletionContext::getCompletionItemsForOneType(AbstractType::Ptr type, bool scoped)
{
    QList<CompletionTreeItemPointer> list;
    QList<DeclarationPair> decls;
    StructureType::Ptr sType = StructureType::Ptr::dynamicCast(type);

    {
        DUChainReadLocker lock;
        if (!sType || !sType->internalContext(m_duContext->topContext())) {
            return QList<CompletionTreeItemPointer>();
        }
        DUContext *current = sType->internalContext(m_duContext->topContext());
        decls = current->allDeclarations(CursorInRevision::invalid(), m_duContext->topContext(), false);
    }

    foreach (DeclarationPair d, decls) {
        MethodDeclaration *md = dynamic_cast<MethodDeclaration *>(d.first);
        if (md && md->accessPolicy() == Declaration::Public) {
            if (!scoped || (scoped && md->isClassMethod())) {

                ADD_NORMAL(d.first, d.second);
            }
        } else if (scoped && dynamic_cast<ModuleDeclaration *>(d.first)) {
            ADD_NORMAL(d.first, d.second);
        }
    }
Example #4
0
void AdaptSignatureAction::execute()
{
    ENSURE_CHAIN_NOT_LOCKED
    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);
    lock.unlock();
    changeParameters.m_ignoreOldText = true;
    changes.addChange(changeParameters);
    changes.setReplacementPolicy(DocumentChangeSet::WarnOnFailedChange);
    DocumentChangeSet::ChangeResult result = changes.applyAllChanges();
    if (!result) {
        KMessageBox::error(nullptr, i18n("Failed to apply changes: %1", result.m_failureReason));
    }
    emit executed(this);

    foreach(RenameAction * renAct, m_renameActions) {
        renAct->execute();
    }
}
Example #5
0
void ADLHelper::addArgumentType(const AbstractType::Ptr typePtr)
{
    if(m_alreadyProcessed.contains(typePtr.data()))
      return;

    if (typePtr)
    {
#ifdef DEBUG_ADL
        qCDebug(CPPDUCHAIN) << "    added argument type " << typePtr->toString() << " to ADL lookup";
#endif
        // the enumeration and enumerator types are not part of the TypeVisitor interface
        switch (typePtr->whichType())
        {
        case AbstractType::TypeEnumeration:
        {
            EnumerationType* specificType = fastCast<EnumerationType*>(typePtr.data());
            if (specificType)
            {
                Declaration * enumDecl = specificType->declaration(m_topContext.data());
                addDeclarationScopeIdentifier(enumDecl);
            }
            break;
        }
        case AbstractType::TypeEnumerator:
        {
            if (m_templateArgsDepth == 0)
            {
                EnumeratorType* specificType = fastCast<EnumeratorType*>(typePtr.data());
                if (specificType)
                {
                    // use the enumeration context for the enumerator value declaration to find out the namespace
                    Declaration * enumeratorDecl = specificType->declaration(m_topContext.data());
                    if (enumeratorDecl) {
                        DUContext * enumContext = enumeratorDecl->context();
                        if (enumContext) {
                            addAssociatedNamespace(enumContext->scopeIdentifier(false));
                        }
                    }
                }
            }
            break;
        }
        default:
            typePtr->accept(&m_typeVisitor);
        }
    }

    m_alreadyProcessed.insert(typePtr.data());
}
void ContextBuilder::openContextForStatementList( const QList<Ast*>& l, DUContext::ContextType /*type*/)
{
    if ( l.count() > 0 )
    {
        Ast* first = l.first();
        Ast* last = l.last();
        Q_ASSERT(first->hasUsefulRangeInformation); // TODO remove this
        RangeInRevision range(RangeInRevision(first->startLine - 1, first->startCol, last->endLine + 1, 10000));
        DUContext* rangectx = openContext(first, range, DUContext::Other );
        kDebug() << " +++ opening context (stmlist): " << range.castToSimpleRange();
        addImportedContexts();
        visitNodeList( l );
        closeContext();
        kDebug() << " --- closed context (stmlist): line " << rangectx->range().castToSimpleRange();
    }
}
Example #7
0
void TestDUChain::testVirtualMemberFunction()
{
    //Forward-declarations with "struct" or "class" are considered equal, so make sure the override is detected correctly.
    TestFile file("struct S {}; struct A { virtual S* ret(); }; struct B : public A { virtual S* ret(); };", "cpp");
    QVERIFY(file.parseAndWait());

    DUChainReadLocker lock;
    DUContext* top = file.topContext().data();
    QVERIFY(top);

    QCOMPARE(top->childContexts().count(), 3);
    QCOMPARE(top->localDeclarations().count(), 3);
    QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1);
    Declaration* decl = top->childContexts()[2]->localDeclarations()[0];
    QCOMPARE(decl->identifier(), Identifier("ret"));
    QVERIFY(DUChainUtils::getOverridden(decl));
}
Example #8
0
void TestDUChainMultipleFiles::testForeachImportedIdentifier()
{
    // see https://bugs.kde.org/show_bug.cgi?id=269369

    TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts;

    TestProject* project = new TestProject;
    m_projectController->clearProjects();
    m_projectController->addProject(project);

    // build dependency
    TestFile f1("<? class SomeIterator implements Countable, Iterator { }", "php", project);
    f1.parse(features);
    f1.waitForParsed();

    TestFile f2("<?\n"
                "class A {\n"
                "  public function foo() { $i = $this->bar(); foreach($i as $a => $b) {} } \n"
                "  public function bar() { $a = new SomeIterator(); return $a; }\n"
                " }\n", "php", project);

    for(int i = 0; i < 2; ++i) {
        if (i > 0) {
            features = static_cast<TopDUContext::Features>(features | TopDUContext::ForceUpdate);
        }
        f2.parse(features);
        f2.waitForParsed();
        QTest::qWait(100);

        DUChainWriteLocker lock(DUChain::lock());
        DUContext* ACtx = f2.topContext()->childContexts().first();
        QVERIFY(ACtx);
        Declaration* iDec = ACtx->childContexts().at(1)->localDeclarations().first();
        QVERIFY(iDec);
        Declaration* SomeIteratorDec = f1.topContext()->localDeclarations().first();
        QVERIFY(SomeIteratorDec);
        if (i == 0) {
            QEXPECT_FAIL("", "needs a full two-pass (i.e. get rid of PreDeclarationBuilder)", Continue);
        }
        QVERIFY(iDec->abstractType()->equals(SomeIteratorDec->abstractType().constData()));
        QVERIFY(f2.topContext()->imports(f1.topContext(), CursorInRevision(0, 0)));
    }
}
Example #9
0
void ADLTypeVisitor::endVisit(const FunctionType * /*type*/)
{
    // return type and argument types are handled by FunctionType::accept0

    // here we process the namespace of the function name (or containing class), if any

    /* at the bottom of 3.4.2.2 we find the following:

    In addition, if the argument is the name or address of a set of overloaded functions and/or function tem-
    plates, its associated classes and namespaces are the union of those associated with each of the members of
    the set: the namespace in which the function or function template is defined and the classes and namespaces
    associated with its (non-dependent) parameter types and return type.
    */

    if (m_helper.m_possibleFunctionName.data() && m_helper.m_possibleFunctionName.data()->isFunctionDeclaration())
    {
        Declaration * declaration = m_helper.m_possibleFunctionName.data();

#ifdef DEBUG_ADL
        qCDebug(CPPDUCHAIN) << "    function name = " << declaration->toString() << " ; identifier = " << declaration->qualifiedIdentifier().toString();
#endif

        // start going towards the global scope until we match an interesting name
        // note that calling addDeclarationScopeIdentifier does not work because for some reason
        // for function names DUContext::scopeIdentifier returns the function name instead of the
        // name of the function's scope
        DUContext* context = declaration->context();
        while (context) {
            if (Declaration* decl = context->owner())
            {
              if (context->type() == DUContext::Namespace)
              {
                  m_helper.addAssociatedNamespace(decl->qualifiedIdentifier());
                  break;
              } else if (context->type() == DUContext::Class) {
                  m_helper.addAssociatedClass(decl);
                  break;
              }
            }
            context = context->parentContext();
        }
    }
}
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;
}
Example #11
0
void TestDUChain::testAutoTypeDeduction()
{
    TestFile file("const volatile auto foo = 5;\n", "cpp");
    QVERIFY(file.parseAndWait());

    DUChainReadLocker lock;

    DUContext* ctx = file.topContext().data();
    QVERIFY(ctx);
    QCOMPARE(ctx->localDeclarations().size(), 1);
    QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1);
    Declaration* decl = ctx->findDeclarations(QualifiedIdentifier("foo"))[0];
    QCOMPARE(decl->identifier(), Identifier("foo"));
#if CINDEX_VERSION_MINOR < 31
    QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue);
#endif
    QVERIFY(decl->type<IntegralType>());
#if CINDEX_VERSION_MINOR < 31
    QCOMPARE(decl->toString(), QStringLiteral("const volatile auto foo"));
#else
    QCOMPARE(decl->toString(), QStringLiteral("const volatile int foo"));
#endif
}
Example #12
0
void TestDUChain::testBaseClasses()
{
    TestFile file("class Base {}; class Inherited : public Base {};", "cpp");
    QVERIFY(file.parseAndWait());

    DUChainReadLocker lock;
    DUContext* top = file.topContext().data();
    QVERIFY(top);

    QCOMPARE(top->localDeclarations().count(), 2);
    Declaration* baseDecl = top->localDeclarations().first();
    QCOMPARE(baseDecl->identifier(), Identifier("Base"));

    ClassDeclaration* inheritedDecl = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[1]);
    QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited"));

    QVERIFY(inheritedDecl);
    QCOMPARE(inheritedDecl->baseClassesSize(), 1u);

    QCOMPARE(baseDecl->uses().count(), 1);
    QCOMPARE(baseDecl->uses().first().count(), 1);
    QCOMPARE(baseDecl->uses().first().first(), RangeInRevision(0, 40, 0, 44));
}
Example #13
0
void TestDUChain::testTypeDeductionInTemplateInstantiation()
{
    // see: http://clang-developers.42468.n3.nabble.com/RFC-missing-libclang-query-functions-features-td2504253.html
    TestFile file("template<typename T> struct foo { T member; } foo<int> f; auto i = f.member;", "cpp");
    QVERIFY(file.parseAndWait());

    DUChainReadLocker lock;

    DUContext* ctx = file.topContext().data();
    QVERIFY(ctx);
    QCOMPARE(ctx->localDeclarations().size(), 3);
    Declaration* decl = 0;

    // check 'foo' declaration
    decl = ctx->localDeclarations()[0];
    QVERIFY(decl);
    QCOMPARE(decl->identifier(), Identifier("foo"));

    // check type of 'member' inside declaration-scope
    QCOMPARE(ctx->childContexts().size(), 1);
    DUContext* fooCtx = ctx->childContexts().first();
    QVERIFY(fooCtx);
    // Should there really be two declarations?
    QCOMPARE(fooCtx->localDeclarations().size(), 2);
    decl = fooCtx->localDeclarations()[1];
    QCOMPARE(decl->identifier(), Identifier("member"));

    // check type of 'member' in definition of 'f'
    decl = ctx->localDeclarations()[1];
    QCOMPARE(decl->identifier(), Identifier("f"));
    decl = ctx->localDeclarations()[2];
    QCOMPARE(decl->identifier(), Identifier("i"));
#if CINDEX_VERSION_MINOR < 31
    QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue);
#endif
    QVERIFY(decl->type<IntegralType>());
}
Example #14
0
void TestDUChain::testReparse()
{
    TestFile file("int main() { int i = 42; return i; }", "cpp");
    file.parse(TopDUContext::AllDeclarationsContextsAndUses);

    DeclarationPointer mainDecl;
    DeclarationPointer iDecl;
    for (int i = 0; i < 3; ++i) {
        QVERIFY(file.waitForParsed(500));
        DUChainReadLocker lock;
        QVERIFY(file.topContext());
        QCOMPARE(file.topContext()->childContexts().size(), 1);
        QCOMPARE(file.topContext()->localDeclarations().size(), 1);
        DUContext *exprContext = file.topContext()->childContexts().first()->childContexts().first();
        QCOMPARE(exprContext->localDeclarations().size(), 1);

        if (i) {
            QVERIFY(mainDecl);
            QCOMPARE(mainDecl.data(), file.topContext()->localDeclarations().first());

            QVERIFY(iDecl);
            QCOMPARE(iDecl.data(), exprContext->localDeclarations().first());
        }
        mainDecl = file.topContext()->localDeclarations().first();
        iDecl = exprContext->localDeclarations().first();

        QVERIFY(mainDecl->uses().isEmpty());
        QCOMPARE(iDecl->uses().size(), 1);
        QCOMPARE(iDecl->uses().begin()->size(), 1);

        if (i == 1) {
            file.setFileContents("int main()\n{\nfloat i = 13; return i - 5;\n}\n");
        }

        file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive));
    }
}
void CorrectionFileGenerator::addHint(const QString &typeCode, const QStringList &modules, Declaration *forDeclaration,
                                      CorrectionFileGenerator::HintType hintType)
{
    if ( ! forDeclaration || ! forDeclaration->context() ) {
        qCWarning(KDEV_PYTHON_CODEGEN) << "Declaration does not have context!" << (forDeclaration ? forDeclaration->toString() : "");
        return;
    }

    DUContext* context = forDeclaration->context();
    if ( context->type() == DUContext::Function ) {
        auto otherImporters = context->importers();
        if ( otherImporters.isEmpty() ) {
            return;
        }
        context = otherImporters.first();
    }

    // We're in a class if the context of the declaration is a Class or if its
    // parent context is a class. This is because a function body has a context
    // of type Other.
    bool inClass = context->type() == DUContext::Class
            || (context->parentContext() && context->parentContext()->type() == DUContext::Class);

    // If the declaration is part of the function's arguments or it's parent
    // context is one of a function.
    bool inFunction = context->type() == DUContext::Function
            || (context->owner() && context->owner()->abstractType()->whichType() == AbstractType::TypeFunction);

    qCDebug(KDEV_PYTHON_CODEGEN) << "Are we in a class: " << inClass;
    qCDebug(KDEV_PYTHON_CODEGEN) << "Are we in a function: " << inFunction;

    QString enclosingClassIdentifier, enclosingFunctionIdentifier;

    if ( context->owner() ) {
        if ( inClass && inFunction ) {
            Declaration *functionDeclaration = context->owner();

            enclosingClassIdentifier = functionDeclaration->context()->owner()->identifier().identifier().str();
            enclosingFunctionIdentifier = functionDeclaration->identifier().identifier().str();
        }
        else if ( inClass ) {
            enclosingClassIdentifier = context->owner()->identifier().identifier().str();
        }
        else if ( inFunction ) {
            enclosingFunctionIdentifier = context->owner()->identifier().identifier().str();
        }
    }

    qCDebug(KDEV_PYTHON_CODEGEN) << "Enclosing class: " << enclosingClassIdentifier;
    qCDebug(KDEV_PYTHON_CODEGEN) << "Enclosing function: " << enclosingFunctionIdentifier;

    QString declarationIdentifier = forDeclaration->identifier().identifier().str();

    bool foundClassDeclaration = false;
    bool foundFunctionDeclaration = false;

    QString functionIdentifier;

    if ( hintType == FunctionReturnHint ) {
        functionIdentifier = declarationIdentifier;
    }
    else if ( hintType == LocalVariableHint ) {
        functionIdentifier = enclosingFunctionIdentifier;
    }

    int line = findStructureFor(enclosingClassIdentifier, functionIdentifier);

    if ( line == -1 ) {
        line = findStructureFor(enclosingClassIdentifier, QString());
    }
    else if ( inFunction || hintType == FunctionReturnHint ) {
        foundFunctionDeclaration = true;
    }

    if ( line == -1 ) {
        line = findStructureFor(QString(), QString());
    }
    else if ( inClass ) {
        foundClassDeclaration = true;
    }

    qCDebug(KDEV_PYTHON_CODEGEN) << "Found class declaration: " << foundClassDeclaration << enclosingClassIdentifier;
    qCDebug(KDEV_PYTHON_CODEGEN) << "Found function declaration: " << foundFunctionDeclaration << functionIdentifier;
    qCDebug(KDEV_PYTHON_CODEGEN) << "Line: " << line;

    int indentsForNextStatement = m_fileIndents->indentForLine(line);

    if ( foundClassDeclaration ) {
        indentsForNextStatement += DEFAULT_INDENT_LEVEL;
    }

    QStringList newCode;
    if ( inClass ) {
        if ( ! foundClassDeclaration ) {
            QString classDeclaration = createStructurePart(enclosingClassIdentifier, ClassType);
            classDeclaration.prepend(QString(indentsForNextStatement, ' '));

            newCode.append(classDeclaration);
            indentsForNextStatement += DEFAULT_INDENT_LEVEL;
        }
        else {
            line++;
        }
    }

    if ( inFunction || hintType == FunctionReturnHint ) {
        if ( ! foundFunctionDeclaration ) {
            QString functionDeclaration;
            if ( inClass ) {
                functionDeclaration = createStructurePart(functionIdentifier, MemberFunctionType);
            }
            else {
                functionDeclaration = createStructurePart(functionIdentifier, FunctionType);
            }
            functionDeclaration.prepend(QString(indentsForNextStatement, ' '));

            newCode.append(functionDeclaration);
            indentsForNextStatement += DEFAULT_INDENT_LEVEL;
        }
        else {
            line++;
        }
    }

    if ( foundFunctionDeclaration && ! foundClassDeclaration ) {
        indentsForNextStatement += DEFAULT_INDENT_LEVEL;
    }
    QString hintCode;
    if ( hintType == FunctionReturnHint ) {
        hintCode = "returns = " + typeCode;
    }
    else if ( hintType == LocalVariableHint ) {
        hintCode = "l_" + declarationIdentifier + " = " + typeCode;
    }
    qCDebug(KDEV_PYTHON_CODEGEN) << "Hint code: " << hintCode;
    hintCode.prepend(QString(indentsForNextStatement, ' '));
    newCode.append(hintCode);

    for ( int i = 0; i < newCode.length(); i++ ) {
        m_code.insert(line + i, newCode.at(i));
    }

    // We safely insert any import declaration at the top
    foreach ( const QString &moduleName, modules ) {
        bool importExists = false;
        foreach (const QString &line, m_code) {
            if ( ! line.startsWith("import") && ! line.startsWith("from") && ! line.isEmpty() ) {
                break;
            }

            // In both import ... and from ... import ..., the second part is what we want
            if ( line.section(' ', 1, 1, QString::SectionSkipEmpty) == moduleName.trimmed() ) {
                importExists = true;
            }
        }

        if ( ! importExists ) {
            m_code.prepend("import " + moduleName.trimmed());
        }
    }
Example #16
0
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"));
  }
}
Example #17
0
void TestDUChain::testNamespace()
{
    TestFile file("namespace foo { struct bar { int baz; }; }\n"
                  "int main() { foo::bar myBar; }\n", "cpp");
    QVERIFY(file.parseAndWait());

    DUChainReadLocker lock;
    QVERIFY(file.topContext());
    QCOMPARE(file.topContext()->localDeclarations().size(), 2);
    auto fooDecl = file.topContext()->localDeclarations().first();
    QVERIFY(fooDecl->internalContext());
    QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 1);

    DUContext* top = file.topContext().data();
    DUContext* mainCtx = file.topContext()->childContexts().last();

    auto foo = top->localDeclarations().first();
    QCOMPARE(foo->qualifiedIdentifier().toString(), QString("foo"));

    DUContext* fooCtx = file.topContext()->childContexts().first();
    QCOMPARE(fooCtx->localScopeIdentifier().toString(), QString("foo"));
    QCOMPARE(fooCtx->scopeIdentifier(true).toString(), QString("foo"));
    QCOMPARE(fooCtx->localDeclarations().size(), 1);
    auto bar = fooCtx->localDeclarations().first();
    QCOMPARE(bar->qualifiedIdentifier().toString(), QString("foo::bar"));
    QCOMPARE(fooCtx->childContexts().size(), 1);

    DUContext* barCtx = fooCtx->childContexts().first();
    QCOMPARE(barCtx->localScopeIdentifier().toString(), QString("bar"));
    QCOMPARE(barCtx->scopeIdentifier(true).toString(), QString("foo::bar"));
    QCOMPARE(barCtx->localDeclarations().size(), 1);
    auto baz = barCtx->localDeclarations().first();
    QCOMPARE(baz->qualifiedIdentifier().toString(), QString("foo::bar::baz"));

    for (auto ctx : {top, mainCtx}) {
        QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1);
        QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar")).size(), 1);
        QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar::baz")).size(), 1);
    }
}
Example #18
0
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);
  }
}