///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; }
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(); } } }
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()); } }
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); } }