void QuickOpenPlugin::quickOpenDeclaration() { if(jumpToSpecialObject()) return; KDevelop::DUChainReadLocker lock( DUChain::lock() ); Declaration* decl = cursorDeclaration(); if(!decl) { qCDebug(PLUGIN_QUICKOPEN) << "Found no declaration for cursor, cannot jump"; return; } decl->activateSpecialization(); IndexedString u = decl->url(); KTextEditor::Cursor c = decl->rangeInCurrentRevision().start(); if(u.isEmpty()) { qCDebug(PLUGIN_QUICKOPEN) << "Got empty url for declaration" << decl->toString(); return; } lock.unlock(); core()->documentController()->openDocument(u.toUrl(), c); }
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(); } }
IndexedString ClangIndex::translationUnitForUrl(const IndexedString& url) { { // try explicit pin data first QMutexLocker lock(&m_mappingMutex); auto tu = m_tuForUrl.find(url); if (tu != m_tuForUrl.end()) { if (!QFile::exists(tu.value().str())) { // TU doesn't exist, unpin m_tuForUrl.erase(tu); return url; } return tu.value(); } } // if no explicit pin data is available, follow back the duchain import chain { DUChainReadLocker lock; TopDUContext* top = DUChain::self()->chainForDocument(url); if (top) { TopDUContext* tuTop = top; QSet<TopDUContext*> visited; while(true) { visited.insert(tuTop); TopDUContext* next = nullptr; auto importers = tuTop->indexedImporters(); foreach(IndexedDUContext ctx, importers) { if (ctx.data()) { next = ctx.data()->topContext(); break; } } if (!next || visited.contains(next)) { break; } tuTop = next; } if (tuTop != top) { return tuTop->url(); } } } // otherwise, fallback to a simple buddy search for headers if (ClangHelpers::isHeader(url.str())) { foreach(const QUrl& buddy, DocumentFinderHelpers::getPotentialBuddies(url.toUrl(), false)) { const QString buddyPath = buddy.toLocalFile(); if (QFile::exists(buddyPath)) { return IndexedString(buddyPath); } } }
void UseBuilder::visitUnaryExpression( UnaryExpressionAst* node ) { IndexedString includeFile = getIncludeFileForNode(node, m_editor); if ( !includeFile.isEmpty() ) { QualifiedIdentifier identifier(includeFile.str()); DUChainWriteLocker lock(DUChain::lock()); foreach ( Declaration* dec, currentContext()->topContext()->findDeclarations(identifier) ) { if ( dec->kind() == Declaration::Import ) { newUse(node->includeExpression, DeclarationPointer(dec)); return; } } }
FileCodeRepresentation(const IndexedString& document) : m_document(document) { QString localFile(document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::ReadOnly) ) { data = QString::fromLocal8Bit(file.readAll()); lineData = data.split('\n'); } m_exists = file.exists(); }
void computeHash() const { Q_ASSERT(dynamic); //this must stay thread-safe(may be called by multiple threads at a time) //The thread-safety is given because all threads will have the same result, and it will only be written once at the end. uint hash = m_identifier.hash(); FOREACH_FUNCTION_STATIC(const IndexedTypeIdentifier& templateIdentifier, templateIdentifiers) hash = hash * 13 + templateIdentifier.hash(); hash += m_unique; m_hash = hash; }
CodeRepresentation::Ptr createCodeRepresentation(const IndexedString& path) { if(artificialCodeRepresentationExists(path)) return CodeRepresentation::Ptr(new StringCodeRepresentation(representationForPath(path))); IDocument* document = ICore::self()->documentController()->documentForUrl(path.toUrl()); if(document && document->textDocument()) return CodeRepresentation::Ptr(new EditorCodeRepresentation(document->textDocument())); else return CodeRepresentation::Ptr(new FileCodeRepresentation(path)); }
//Return the representation for the given URL if it exists, or an empty pointer otherwise static QExplicitlySharedDataPointer<ArtificialStringData> representationForPath(const IndexedString& path) { if(artificialStrings.contains(path)) return artificialStrings[path]; else { IndexedString constructedPath(CodeRepresentation::artificialPath(path.str())); if(artificialStrings.contains(constructedPath)) return artificialStrings[constructedPath]; else return QExplicitlySharedDataPointer<ArtificialStringData>(); } }
//Return the representation for the given URL if it exists, or an empty pointer otherwise KSharedPtr<ArtificialStringData> representationForUrl(const IndexedString& url) { if(artificialStrings.contains(url)) return artificialStrings[url]; else { IndexedString constructedUrl(CodeRepresentation::artificialUrl(url.str())); if(artificialStrings.contains(constructedUrl)) return artificialStrings[constructedUrl]; else return KSharedPtr<ArtificialStringData>(); } }
IndexedString ClangIndex::translationUnitForUrl(const IndexedString& url) { { // try explicit pin data first QMutexLocker lock(&m_mappingMutex); auto tu = m_tuForUrl.find(url); if (tu != m_tuForUrl.end()) { if (!QFile::exists(tu.value().str())) { // TU doesn't exist, unpin m_tuForUrl.erase(tu); return url; } return tu.value(); } } // otherwise, fallback to a simple buddy search for headers if (ClangHelpers::isHeader(url.str())) { foreach(const QUrl& buddy, DocumentFinderHelpers::getPotentialBuddies(url.toUrl(), false)) { const QString buddyPath = buddy.toLocalFile(); if (QFile::exists(buddyPath)) { return IndexedString(buddyPath); } } }
void ApplyChangesWidget::addDocuments(const IndexedString & original) { int idx=d->m_files.indexOf(original); if(idx<0) { QWidget * w = new QWidget; d->m_documentTabs->addTab(w, original.str()); d->m_documentTabs->setCurrentWidget(w); d->m_files.insert(d->m_index, original); d->createEditPart(original); } else { d->m_index=idx; } }
QList< ReferencedTopDUContext > ParseSession::contextForThisPackage(IndexedString package) { QList<ReferencedTopDUContext> contexts; QUrl url = package.toUrl(); QDir path(url.adjusted(QUrl::RemoveFilename).path()); if(path.exists()) { int priority = BackgroundParser::WorstPriority; if(!forExport) priority = -1; //import this package as soon as possible else if(m_priority<=-1) priority = BackgroundParser::WorstPriority-2;//all needed files should be scheduled already else priority = m_priority;//currently parsejob does not get created in this cases to reduce recursion QStringList files = path.entryList(QStringList("*.go"), QDir::Files | QDir::NoSymLinks); bool shouldReparse=false; for(QString filename : files) { filename = path.filePath(filename); QFile file(filename); if(!file.exists()) continue; if(forExport && filename.endsWith("_test.go")) continue; IndexedString url(filename); DUChainReadLocker lock; ReferencedTopDUContext context = DUChain::self()->chainForDocument(url); lock.unlock(); if(context) contexts.append(context); else { if(scheduleForParsing(url, priority, (TopDUContext::Features)(TopDUContext::ForceUpdate | TopDUContext::AllDeclarationsAndContexts))) shouldReparse=true; } } if(shouldReparse) scheduleForParsing(m_document, priority+1, (TopDUContext::Features)(m_features | TopDUContext::ForceUpdate)); } return contexts; }
InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(const IndexedString& file, const QString& text) : m_file(file) { if(m_file.toUrl().isRelative()) { m_file = IndexedString(CodeRepresentation::artificialUrl(file.str())); int idx = 0; while(artificialStrings.contains(m_file)) { ++idx; m_file = IndexedString(CodeRepresentation::artificialUrl(QString("%1_%2").arg(idx).arg(file.str()))); } } Q_ASSERT(!artificialStrings.contains(m_file)); artificialStrings.insert(m_file, KSharedPtr<ArtificialStringData>(new ArtificialStringData(text))); }
void ApplyChangesWidgetPrivate::createEditPart(const IndexedString & file) { QWidget * widget = m_documentTabs->currentWidget(); Q_ASSERT(widget); QVBoxLayout *m=new QVBoxLayout(widget); QSplitter *v=new QSplitter(widget); m->addWidget(v); QUrl url = file.toUrl(); QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(url); KParts::ReadWritePart* part=KMimeTypeTrader::self()->createPartInstanceFromQuery<KParts::ReadWritePart>(mimetype.name(), widget, widget); KTextEditor::Document* document=qobject_cast<KTextEditor::Document*>(part); Q_ASSERT(document); Q_ASSERT(document->action("file_save")); document->action("file_save")->setEnabled(false); m_editParts.insert(m_index, part); //Open the best code representation, even if it is artificial CodeRepresentation::Ptr repr = createCodeRepresentation(file); if(!repr->fileExists()) { const QString templateName = QDir::tempPath() + QLatin1Char('/') + url.fileName().split('.').last(); QTemporaryFile * temp(new QTemporaryFile(templateName)); temp->open(); temp->write(repr->text().toUtf8()); temp->close(); url = QUrl::fromLocalFile(temp->fileName()); m_temps << temp; } m_editParts[m_index]->openUrl(url); v->addWidget(m_editParts[m_index]->widget()); v->setSizes(QList<int>() << 400 << 100); }
InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(const IndexedString& file, const QString& text) : m_file(file) { // make it simpler to use this by converting relative strings into artificial paths if(QUrl(m_file.str()).isRelative()) { m_file = IndexedString(CodeRepresentation::artificialPath(file.str())); int idx = 0; while(artificialStrings.contains(m_file)) { ++idx; m_file = IndexedString(CodeRepresentation::artificialPath(QStringLiteral("%1_%2").arg(idx).arg(file.str()))); } } Q_ASSERT(!artificialStrings.contains(m_file)); artificialStrings.insert(m_file, QExplicitlySharedDataPointer<ArtificialStringData>(new ArtificialStringData(text))); }
bool KompareWidgets::compare(const IndexedString & original, const QString & modified, QWidget * widget, int index) { //If there is no current part created, Create it if( !d->partExists(index) && !d->createWidget(index, widget)) return false; //Prepare the part KParts::Part * part = d->m_parts[index]; KompareInterface * ipart = qobject_cast<KompareInterface * >(part); Q_ASSERT(part); part->widget()->setVisible(true); part->widget()->resize(part->widget()->parentWidget()->size()); part->widget()->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); //Compare ipart->compareFileString(original.toUrl(), modified); //Set to used d->m_usedWidgets[index] = true; return true; }
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; }
bool UsesCollector::shouldRespectFile(IndexedString document) { return (bool)ICore::self()->projectController()->findProjectForUrl(document.toUrl()) || (bool)ICore::self()->documentController()->documentForUrl(document.toUrl()); }
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; }
///TODO: share code with TypeBuilder void TypeASTVisitor::visitSimpleTypeSpecifier(SimpleTypeSpecifierAST *node) { if(m_stopSearch) return; Cpp::FindDeclaration find( m_context, m_source, m_flags, m_context->range().end ); find.openQualifiedIdentifier(false); // Don't forget the modifiers! uint modifiers = AbstractType::NoModifiers; if (node->cv) { const ListNode<uint> *it = node->cv->toFront(); const ListNode<uint> *end = it; do { int kind = m_session->token_stream->kind(it->element); switch (kind) { case Token_const: modifiers |= AbstractType::ConstModifier; break; case Token_volatile: modifiers |= AbstractType::VolatileModifier; break; default: //qCDebug(CPPDUCHAIN) << "Unknown modifier token" << kind; break; } it = it->next; } while (it != end); } if (node->integrals) { uint type = IntegralType::TypeNone; const ListNode<uint> *it2 = node->integrals->toFront(); const ListNode<uint> *end = it2; do { int kind = m_session->token_stream->kind(it2->element); switch (kind) { case Token_char: type = IntegralType::TypeChar; break; case Token_char16_t: type = IntegralType::TypeChar16_t; break; case Token_char32_t: type = IntegralType::TypeChar32_t; break; case Token_wchar_t: type = IntegralType::TypeWchar_t; break; case Token_bool: type = IntegralType::TypeBoolean; break; case Token_short: modifiers |= AbstractType::ShortModifier; break; case Token_int: type = IntegralType::TypeInt; break; case Token_long: if (modifiers & AbstractType::LongModifier) modifiers |= AbstractType::LongLongModifier; else modifiers |= AbstractType::LongModifier; break; case Token_signed: modifiers |= AbstractType::SignedModifier; break; case Token_unsigned: modifiers |= AbstractType::UnsignedModifier; break; case Token_float: type = IntegralType::TypeFloat; break; case Token_double: type = IntegralType::TypeDouble; break; case Token_void: type = IntegralType::TypeVoid; break; } it2 = it2->next; } while (it2 != end); if(type == IntegralType::TypeNone) type = IntegralType::TypeInt; //Happens, example: "unsigned short" KDevelop::IntegralType::Ptr integral ( new KDevelop::IntegralType(type) ); integral->setModifiers(modifiers); m_type = integral.cast<AbstractType>(); m_typeId = QualifiedIdentifier(integral->toString()); } else if (node->isTypeof || node->isDecltype) { if (node->expression) { bool isDecltypeInParen = false; if (node->isDecltype && node->expression->kind == AST::Kind_PrimaryExpression) { int startPosition = m_session->token_stream->position(node->expression->start_token); static IndexedString paren("("); isDecltypeInParen = m_session->contentsVector()[startPosition] == paren.index(); } ExpressionParser parser(false, false, isDecltypeInParen); node->expression->ducontext = const_cast<DUContext*>(m_context); ExpressionEvaluationResult result = parser.evaluateType(node->expression, m_session); m_type = result.type.abstractType(); m_typeId = QualifiedIdentifier(result.toString()); { LOCKDUCHAIN; // Transform specific constants like '5' into their type 'int' m_type = TypeUtils::removeConstants(m_type, m_source); } // make reference for decltype in additional parens - but only if it's not already a reference // see spec 7.1.6/4 if (isDecltypeInParen && m_type && !TypeUtils::isReferenceType(m_type)) { // type might already be a ref type ReferenceType::Ptr refType = ReferenceType::Ptr(new ReferenceType); refType->setBaseType(m_type); m_type = refType.cast<AbstractType>(); ///TODO: anything todo with m_typeId ? } if(m_visitor) m_visitor->parse(node->expression); // Give the visitor a chance to build uses } } { LOCKDUCHAIN; find.closeQualifiedIdentifier(); m_declarations = find.lastDeclarations(); if(!m_declarations.isEmpty() && m_declarations[0]) m_type = m_declarations[0]->abstractType(); } visit(node->name); }