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