void DeclarationBuilder::visitFuncDeclaration(IFunctionDeclaration *node)
{
	TypeBuilder::visitFuncDeclaration(node);
	DUChainWriteLocker lock;
	if(inClassScope)
	{
		ClassFunctionDeclaration *newMethod = openDefinition<ClassFunctionDeclaration>(node->getName(), node);
		if(node->getComment())
			newMethod->setComment(QString::fromUtf8(node->getComment()));
		newMethod->setKind(KDevelop::Declaration::Type);
		lock.unlock();
		ContextBuilder::visitFuncDeclaration(node);
		lock.lock();
		closeDeclaration();
		newMethod->setInternalContext(lastContext());
		newMethod->setType(currentFunctionType);
	}
	else
	{
		FunctionDeclaration *newMethod = openDefinition<FunctionDeclaration>(node->getName(), node);
		if(node->getComment())
			newMethod->setComment(QString::fromUtf8(node->getComment()));
		newMethod->setKind(KDevelop::Declaration::Type);
		lock.unlock();
		ContextBuilder::visitFuncDeclaration(node);
		lock.lock();
		closeDeclaration();
		newMethod->setInternalContext(lastContext());
		newMethod->setType(currentFunctionType);
	}
}
void DeclarationBuilder::visitEnumDeclaration(IEnumDeclaration *node)
{
	DUChainWriteLocker lock;
	Declaration *e = openDeclaration<Declaration>(node->getName(), node);
	e->setKind(Declaration::Type);
	lock.unlock();
	DeclarationBuilderBase::visitEnumDeclaration(node);
	lock.lock();
	e->setInternalContext(lastContext());
	//e->setAbstractType(lastType());
	closeDeclaration();
}
void DeclarationBuilder::visitDestructor(IDestructor *node)
{
	TypeBuilder::visitDestructor(node);
	DUChainWriteLocker lock;
	ClassFunctionDeclaration *newMethod = openDefinition<ClassFunctionDeclaration>(QualifiedIdentifier("~this"), editorFindRange(node, node));
	if(node->getComment())
		newMethod->setComment(QString::fromUtf8(node->getComment()));
	newMethod->setKind(KDevelop::Declaration::Type);
	lock.unlock();
	ContextBuilder::visitDestructor(node);
	lock.lock();
	closeDeclaration();
	newMethod->setInternalContext(lastContext());
	newMethod->setType(currentFunctionType);
}
Ejemplo n.º 4
0
void DeclarationBuilder::visitModuleStatement(Ast *node)
{
    RangeInRevision range = getNameRange(node);
    QualifiedIdentifier id = getIdentifier(node);
    const QByteArray comment = getComment(node);
    DUContext *ctx = getContainedNameContext(node);

    /* First of all, open the declaration. */
    ModuleDeclaration *decl = reopenDeclaration<ModuleDeclaration>(id, range, ctx, DeclarationKind::Module);
    if (!decl) {
        node->foundProblems = true;
        return;
    }

    // Initialize the declaration.
    DUChainWriteLocker wlock;
    if (!comment.isEmpty()) {
        decl->setComment(comment);
    }
    decl->setIsModule(true);
    decl->clearModuleMixins();
    decl->clearMixers();
    decl->setKind(KDevelop::Declaration::Type);
    m_accessPolicy.push(Declaration::Public);
    m_classDeclarations.push(DeclarationPointer(decl));

    StructureType::Ptr type(new StructureType());
    type->setDeclaration(decl);
    decl->setType(type);
    openType(type);

    openContextForClassDefinition(decl, node);
    decl->setInternalContext(currentContext());
    wlock.unlock();
    DeclarationBuilderBase::visitModuleStatement(node);
    wlock.lock();
    closeContext();

    closeType();
    closeDeclaration();
    m_accessPolicy.pop();
    m_classDeclarations.pop();
}
Ejemplo n.º 5
0
void TestDUChain::testActiveDocumentHasASTAttached()
{
  const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;

    IndexedTopDUContext indexed;
    ClangParsingEnvironment lastEnv;
    {
        TestFile file("int main() {}\n", "cpp");
        auto astFeatures = static_cast<TopDUContext::Features>(features | TopDUContext::AST);
        file.parse(astFeatures);
        file.setKeepDUChainData(true);
        QVERIFY(file.waitForParsed());

        DUChainWriteLocker lock;
        auto top = file.topContext();
        QVERIFY(top);
        auto sessionData = ParseSessionData::Ptr(dynamic_cast<ParseSessionData*>(top->ast().data()));
        lock.unlock();
        ParseSession session(sessionData);
        lock.lock();
        QVERIFY(session.data());
        QVERIFY(top);
        QVERIFY(top->ast());

        indexed = top->indexed();
    }

    DUChain::self()->storeToDisk();

    {
        DUChainWriteLocker lock;
        QVERIFY(!DUChain::self()->isInMemory(indexed.index()));
        QVERIFY(indexed.data());
    }

    QUrl url;
    {
        DUChainReadLocker lock;
        auto ctx = indexed.data();
        QVERIFY(ctx);
        QVERIFY(!ctx->ast());
        url = ctx->url().toUrl();
    }

    // Here the file is already deleted, so clang_parseTranslationUnit2 will fail, but we still get ParseSessionData attached.
    auto document = ICore::self()->documentController()->openDocument(url);
    QVERIFY(document);
    ICore::self()->documentController()->activateDocument(document);

    QApplication::processEvents();
    ICore::self()->languageController()->backgroundParser()->parseDocuments();
    QThread::sleep(1);

    document->close(KDevelop::IDocument::Discard);
    {
        DUChainReadLocker lock;
        auto ctx = indexed.data();
        QVERIFY(ctx);
        QVERIFY(ctx->ast());
    }

    DUChainWriteLocker lock;
    DUChain::self()->removeDocumentChain(indexed.data());
}
Ejemplo n.º 6
0
void TestDUChain::testParsingEnvironment()
{
    const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses;

    IndexedTopDUContext indexed;
    ClangParsingEnvironment lastEnv;
    {
        TestFile file("int main() {}\n", "cpp");
        auto astFeatures = static_cast<TopDUContext::Features>(features | TopDUContext::AST);
        file.parse(astFeatures);
        file.setKeepDUChainData(true);
        QVERIFY(file.waitForParsed());

        DUChainWriteLocker lock;
        auto top = file.topContext();
        QVERIFY(top);
        auto sessionData = ParseSessionData::Ptr(dynamic_cast<ParseSessionData*>(top->ast().data()));
        lock.unlock();
        ParseSession session(sessionData);
        lock.lock();
        QVERIFY(session.data());
        QVERIFY(top);

        auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
            dynamic_cast<ClangParsingEnvironmentFile*>(file.topContext()->parsingEnvironmentFile().data()));

        QCOMPARE(envFile->features(), astFeatures);
        QVERIFY(envFile->featuresSatisfied(astFeatures));
        QCOMPARE(envFile->environmentQuality(), ClangParsingEnvironment::Source);

        // if no environment is given, no update should be triggered
        QVERIFY(!envFile->needsUpdate());

        // same env should also not trigger a reparse
        ClangParsingEnvironment env = session.environment();
        QCOMPARE(env.quality(), ClangParsingEnvironment::Source);
        QVERIFY(!envFile->needsUpdate(&env));

        // but changing the environment should trigger an update
        env.addIncludes(Path::List() << Path("/foo/bar/baz"));
        QVERIFY(envFile->needsUpdate(&env));
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));

        // setting the environment quality higher should require an update
        env.setQuality(ClangParsingEnvironment::BuildSystem);
        QVERIFY(envFile->needsUpdate(&env));
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));

        // changing defines requires an update
        env.addDefines(QHash<QString, QString>{ { "foo", "bar" } });
        QVERIFY(envFile->needsUpdate(&env));

        // but only when changing the defines for the envFile's TU
        const auto barTU = IndexedString("bar.cpp");
        const auto oldTU = env.translationUnitUrl();
        env.setTranslationUnitUrl(barTU);
        QCOMPARE(env.translationUnitUrl(), barTU);
        QVERIFY(!envFile->needsUpdate(&env));
        env.setTranslationUnitUrl(oldTU);
        QVERIFY(envFile->needsUpdate(&env));

        // update it again
        envFile->setEnvironment(env);
        QVERIFY(!envFile->needsUpdate(&env));
        lastEnv = env;

        // now compare against a lower quality environment
        // in such a case, we do not want to trigger an update
        env.setQuality(ClangParsingEnvironment::Unknown);
        env.setTranslationUnitUrl(barTU);
        QVERIFY(!envFile->needsUpdate(&env));

        // even when the environment changes
        env.addIncludes(Path::List() << Path("/lalalala"));
        QVERIFY(!envFile->needsUpdate(&env));

        indexed = top->indexed();
    }

    DUChain::self()->storeToDisk();

    {
        DUChainWriteLocker lock;
        QVERIFY(!DUChain::self()->isInMemory(indexed.index()));
        QVERIFY(indexed.data());
        QVERIFY(DUChain::self()->environmentFileForDocument(indexed));
        auto envFile = QExplicitlySharedDataPointer<ClangParsingEnvironmentFile>(
            dynamic_cast<ClangParsingEnvironmentFile*>(DUChain::self()->environmentFileForDocument(indexed).data()));
        QVERIFY(envFile);

        QCOMPARE(envFile->features(), features);
        QVERIFY(envFile->featuresSatisfied(features));
        QVERIFY(!envFile->needsUpdate(&lastEnv));
        DUChain::self()->removeDocumentChain(indexed.data());
    }
}
Ejemplo n.º 7
0
void DeclarationBuilder::visitClassStatement(Ast *node)
{
    ModuleDeclaration *baseClass = nullptr;
    RangeInRevision range = getNameRange(node);
    const QByteArray comment = getComment(node);
    QualifiedIdentifier id = getIdentifier(node);
    DUContext *ctx = getContainedNameContext(node);

    /* First of all, open the declaration. */
    ModuleDeclaration *decl = reopenDeclaration<ModuleDeclaration>(id, range, ctx, DeclarationKind::Class);
    if (!decl) {
        node->foundProblems = true;
        return;
    }

    // Initialize the declaration.
    DUChainWriteLocker lock;
    if (!comment.isEmpty()) {
        decl->setComment(comment);
    }
    decl->setIsModule(false);
    decl->clearBaseClass();
    decl->clearModuleMixins();
    decl->setKind(KDevelop::Declaration::Type);
    m_accessPolicy.push(Declaration::Public);
    m_classDeclarations.push(DeclarationPointer(decl));

    /*
     * Now let's check for the base class. Ruby does not support multiple
     * inheritance, and the access is always public.
     */
    Node *aux = node->tree;
    node->tree = node->tree->cond;
    if (node->tree) {
        ExpressionVisitor ev(ctx, m_editor);
        lock.unlock();
        ev.visitNode(node);
        DeclarationPointer baseDecl = ev.lastDeclaration();
        lock.lock();
        if (baseDecl) {
            baseClass = dynamic_cast<ModuleDeclaration *>(baseDecl.data());
            if (!baseClass || baseClass->isModule()) {
                appendProblem(
                    node->tree,
                    i18n("TypeError: wrong argument type (expected Class)")
                );
            } else if (baseClass->internalContext()) {
                decl->setBaseClass(baseClass->indexedType());
            }
        }
    }
    node->tree = aux;

    /*  Setup types and go for the class body */
    ClassType::Ptr type(new ClassType());
    type->setDeclaration(decl);
    decl->setType(type);
    openType(type);

    openContextForClassDefinition(decl, node);
    if (baseClass && baseClass->internalContext()) {
        currentContext()->addImportedParentContext(baseClass->internalContext());
    }
    decl->setInternalContext(currentContext());
    lock.unlock();
    DeclarationBuilderBase::visitClassStatement(node);
    lock.lock();
    closeContext();

    closeType();
    closeDeclaration();
    m_classDeclarations.pop();
    m_accessPolicy.pop();
}
Ejemplo n.º 8
0
void DeclarationBuilder::visitMethodStatement(Ast *node)
{
    RangeInRevision range = getNameRange(node);
    QualifiedIdentifier id = getIdentifier(node);
    const QByteArray comment = getComment(node);
    bool injectedContext = false;
    bool instance = true;
    Node *aux = node->tree;

    /*
     * Check if this is a singleton method. If it is so, we have to determine
     * what's the context to be injected in order to get everything straight.
     */
    // TODO: this will change with the introduction of the eigen class.
    node->tree = aux->cond;
    if (valid_children(node->tree)) {
        node->tree = node->tree->l;
        ExpressionVisitor ev(currentContext(), m_editor);
        ev.visitNode(node);
        if (ev.lastType()) {
            DeclarationPointer d = ev.lastDeclaration();
            if (d) {
                if (!d->internalContext()) {
                    DUChainWriteLocker lock;
                    StructureType::Ptr sType = StructureType::Ptr::dynamicCast(ev.lastType());
                    d = (sType) ? sType->declaration(topContext()) : nullptr;
                    instance = true;
                } else
                    instance = false;
                if (d) {
                    injectedContext = true;
                    injectContext(d->internalContext());
                    node->tree = aux->cond->r;
                    id = getIdentifier(node);
                    range = editorFindRange(node, node);
                }
            }
        }
    }
    node->tree = aux;

    // Re-open the declaration.
    bool isClassMethod = (m_injected) ? !m_instance : !instance;
    MethodDeclaration *decl = reopenDeclaration(id, range, isClassMethod);

    DUChainWriteLocker lock;
    if (!comment.isEmpty()) {
        decl->setComment(comment);
    }
    decl->clearYieldTypes();
    decl->setClassMethod(isClassMethod);

    FunctionType::Ptr type = FunctionType::Ptr(new FunctionType());
    if (currentContext()->type() == DUContext::Class) {
        decl->setAccessPolicy(currentAccessPolicy());
    }

    openType(type);
    decl->setInSymbolTable(false);
    decl->setType(type);
    decl->clearDefaultParameters();
    lock.unlock();
    DeclarationBuilderBase::visitMethodStatement(node);
    lock.lock();
    closeDeclaration();
    closeType();

    /*
     * In Ruby, a method returns the last expression if no return expression
     * has been fired. Thus, the type of the last expression has to be mixed
     * into the return type of this method.
     */
    node->tree = aux->l;
    if (node->tree && node->tree->l) {
        node->tree = get_last_expr(node->tree->l);
        if (node->tree->kind != token_return) {
            lock.unlock();
            ExpressionVisitor ev(node->context, m_editor);
            ev.visitNode(node);
            if (ev.lastType()) {
                type->setReturnType(mergeTypes(ev.lastType(), type->returnType()));
            }
            lock.lock();
        }
    }
    node->tree = aux;

    if (!type->returnType()) {
        type->setReturnType(getBuiltinsType(QStringLiteral("NilClass"), currentContext()));
    }
    decl->setType(type);
    decl->setInSymbolTable(true);

    if (injectedContext) {
        closeInjectedContext();
    }
}