void TestParseJob::testSimpleHtml() { TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts; TestProject* project = new TestProject; m_projectController->closeAllProjects(); m_projectController->addProject(project); TestFile f("<html><style>a { color: red; }</style></html>", "html", project); f.parse(features); ReferencedTopDUContext top = f.topContext(); DUChainReadLocker lock; QVERIFY(top); QVERIFY(top->parsingEnvironmentFile()->language() == IndexedString("Css")); QCOMPARE(top->childContexts().count(), 1); }
DeclarationPointer ClangHelpers::findDeclaration(CXSourceLocation location, const ReferencedTopDUContext& top) { if (!top) { // may happen for cyclic includes return {}; } auto cursor = CursorInRevision(ClangLocation(location)); DUChainReadLocker lock; Q_ASSERT(top); if (DUContext *local = top->findContextAt(cursor)) { if (local->owner() && local->owner()->range().contains(cursor)) { return DeclarationPointer(local->owner()); } return DeclarationPointer(local->findDeclarationAt(cursor)); } return {}; }
ReferencedTopDUContext ClangHelpers::buildDUChain(CXFile file, const Imports& imports, const ParseSession& session, TopDUContext::Features features, IncludeFileContexts& includedFiles, ClangIndex* index) { if (includedFiles.contains(file)) { return {}; } // prevent recursion includedFiles.insert(file, {}); // ensure DUChain for imports are build properly foreach(const auto& import, imports.values(file)) { buildDUChain(import.file, imports, session, features, includedFiles, index); } const IndexedString path(QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath()); if (path.isEmpty()) { // may happen when the file gets removed before the job is run return {}; } const auto& environment = session.environment(); bool update = false; UrlParseLock urlLock(path); ReferencedTopDUContext context; { DUChainWriteLocker lock; context = DUChain::self()->chainForDocument(path, &environment); if (!context) { context = ::createTopContext(path, environment); } else { update = true; } includedFiles.insert(file, context); if (update) { auto envFile = ClangParsingEnvironmentFile::Ptr(dynamic_cast<ClangParsingEnvironmentFile*>(context->parsingEnvironmentFile().data())); Q_ASSERT(envFile); if (!envFile->needsUpdate(&environment) && envFile->featuresSatisfied(features)) { return context; } else { //TODO: don't attempt to update if this environment is worse quality than the outdated one if (index && envFile->environmentQuality() < environment.quality()) { index->pinTranslationUnitForUrl(environment.translationUnitUrl(), path); } envFile->setEnvironment(environment); envFile->setModificationRevision(ModificationRevision::revisionForFile(context->url())); } context->clearImportedParentContexts(); } context->setFeatures(features); foreach(const auto& import, imports.values(file)) { Q_ASSERT(includedFiles.contains(import.file)); auto ctx = includedFiles.value(import.file); if (!ctx) { // happens for cyclic imports continue; } context->addImportedParentContext(ctx, import.location); } context->updateImportsCache(); } const auto problems = session.problemsForFile(file); { DUChainWriteLocker lock; context->setProblems(problems); } Builder::visit(session.unit(), file, includedFiles, update); return context; }
void CollectorProgressDialog::processUses(ReferencedTopDUContext context) { DUChainReadLocker lock(DUChain::lock()); if(context.data()) setProcessing(context->url()); }
ReferencedTopDUContext ContextBuilder::buildContexts(Cpp::EnvironmentFilePointer file, AST *node, IncludeFileList* includes, const ReferencedTopDUContext& updateContext, bool removeOldImports) { Q_ASSERT(file); setCompilingContexts(true); { DUChainWriteLocker lock(DUChain::lock()); if(updateContext && (updateContext->parsingEnvironmentFile() && updateContext->parsingEnvironmentFile()->isProxyContext())) { kDebug(9007) << "updating a context " << file->url().str() << " from a proxy-context to a content-context"; updateContext->parsingEnvironmentFile()->setIsProxyContext(false); } } if(editor()->currentUrl() != file->url()) editor()->setCurrentUrl(file->url(), true); ReferencedTopDUContext topLevelContext; { DUChainWriteLocker lock(DUChain::lock()); topLevelContext = updateContext; if( topLevelContext && topLevelContext->smartRange() && !(topLevelContext->parsingEnvironmentFile() && topLevelContext->parsingEnvironmentFile()->isProxyContext())) if (topLevelContext->smartRange()->parentRange()) { //Top-range must have no parent, else something is wrong with the structure kWarning() << *topLevelContext->smartRange() << "erroneously has a parent range" << *topLevelContext->smartRange()->parentRange(); Q_ASSERT(false); } if (topLevelContext) { kDebug(9007) << "ContextBuilder::buildContexts: recompiling"; setRecompiling(true); if (compilingContexts()) { // To here... LockedSmartInterface iface = editor()->smart(); if (iface && topLevelContext->range().textRange() != iface.currentDocument()->documentRange()) { topLevelContext->setRange(SimpleRange(iface.currentDocument()->documentRange())); //This happens the whole file is deleted, and then a space inserted. kDebug(9007) << "WARNING: Top-level context has wrong size: " << topLevelContext->range().textRange() << " should be: " << iface.currentDocument()->documentRange(); } } DUChain::self()->updateContextEnvironment( topLevelContext, const_cast<Cpp::EnvironmentFile*>(file.data() ) ); } else { kDebug(9007) << "ContextBuilder::buildContexts: compiling"; setRecompiling(false); Q_ASSERT(compilingContexts()); LockedSmartInterface iface = editor()->smart(); topLevelContext = new CppDUContext<TopDUContext>(editor()->currentUrl(), iface.currentDocument() ? SimpleRange(iface.currentDocument()->documentRange()) : SimpleRange(SimpleCursor(0,0), SimpleCursor(INT_MAX, INT_MAX)), const_cast<Cpp::EnvironmentFile*>(file.data())); topLevelContext->setSmartRange(editor()->topRange(iface, CppEditorIntegrator::DefinitionUseChain), DocumentRangeObject::Own); topLevelContext->setType(DUContext::Global); topLevelContext->setFlags((TopDUContext::Flags)(TopDUContext::UpdatingContext | topLevelContext->flags())); DUChain::self()->addDocumentChain(topLevelContext); topLevelContext->updateImportsCache(); //Mark that we will use a cached import-structure } setEncountered(topLevelContext); if (includes) { if(removeOldImports) { foreach (const DUContext::Import &parent, topLevelContext->importedParentContexts()) if (!containsContext(*includes, dynamic_cast<TopDUContext*>(parent.context(0)))) topLevelContext->removeImportedParentContext(parent.context(0)); } QList< QPair<TopDUContext*, SimpleCursor> > realIncluded; QList< QPair<TopDUContext*, SimpleCursor> > realTemporaryIncluded; foreach (const LineContextPair &included, *includes) if(!included.temporary) realIncluded << qMakePair(included.context.data(), SimpleCursor(included.sourceLine, 0)); else realTemporaryIncluded << qMakePair(included.context.data(), SimpleCursor(included.sourceLine, 0)); topLevelContext->addImportedParentContexts(realIncluded); topLevelContext->addImportedParentContexts(realTemporaryIncluded, true); topLevelContext->updateImportsCache(); } } { DUChainReadLocker lock(DUChain::lock()); //If we're debugging the current file, dump its preprocessed contents and the AST ifDebugFile( HashedString(file->identity().url().str()), { kDebug() << stringFromContents(editor()->parseSession()->contentsVector()); Cpp::DumpChain dump; dump.dump(node, editor()->parseSession()); } );
ReferencedTopDUContext ClangHelpers::buildDUChain(CXFile file, const Imports& imports, const ParseSession& session, TopDUContext::Features features, IncludeFileContexts& includedFiles, ClangIndex* index, const std::function<bool()>& abortFunction) { if (includedFiles.contains(file)) { return {}; } if (abortFunction && abortFunction()) { return {}; } // prevent recursion includedFiles.insert(file, {}); // ensure DUChain for imports are build properly foreach(const auto& import, imports.values(file)) { buildDUChain(import.file, imports, session, features, includedFiles, index, abortFunction); } const IndexedString path(QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath()); if (path.isEmpty()) { // may happen when the file gets removed before the job is run return {}; } const auto& environment = session.environment(); bool update = false; UrlParseLock urlLock(path); ReferencedTopDUContext context; { DUChainWriteLocker lock; context = DUChain::self()->chainForDocument(path, &environment); if (!context) { context = ::createTopContext(path, environment); } else { update = true; } includedFiles.insert(file, context); if (update) { auto envFile = ClangParsingEnvironmentFile::Ptr(dynamic_cast<ClangParsingEnvironmentFile*>(context->parsingEnvironmentFile().data())); Q_ASSERT(envFile); if (!envFile) return context; /* NOTE: When we are here, then either the translation unit or one of its headers was changed. * Thus we must always update the translation unit to propagate the change(s). * See also: https://bugs.kde.org/show_bug.cgi?id=356327 * This assumes that headers are independent, we may need to improve that in the future * and also update header files more often when other files included therein got updated. */ if (path != environment.translationUnitUrl() && !envFile->needsUpdate(&environment) && envFile->featuresSatisfied(features)) { return context; } else { //TODO: don't attempt to update if this environment is worse quality than the outdated one if (index && envFile->environmentQuality() < environment.quality()) { index->pinTranslationUnitForUrl(environment.translationUnitUrl(), path); } envFile->setEnvironment(environment); envFile->setModificationRevision(ModificationRevision::revisionForFile(context->url())); } context->clearImportedParentContexts(); } context->setFeatures(features); foreach(const auto& import, imports.values(file)) { auto ctx = includedFiles.value(import.file); if (!ctx) { // happens for cyclic imports continue; } context->addImportedParentContext(ctx, import.location); } context->updateImportsCache(); } const auto problems = session.problemsForFile(file); { DUChainWriteLocker lock; context->setProblems(problems); } Builder::visit(session.unit(), file, includedFiles, update); DUChain::self()->emitUpdateReady(path, context); return context; }