diff --git a/language/codegen/coderepresentation.cpp b/language/codegen/coderepresentation.cpp index b3aefa7870..05f3d29683 100644 --- a/language/codegen/coderepresentation.cpp +++ b/language/codegen/coderepresentation.cpp @@ -1,349 +1,367 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "coderepresentation.h" #include #include #include #include #include #include #include namespace KDevelop { static bool onDiskChangesForbidden = false; QString CodeRepresentation::rangeText(KTextEditor::Range range) const { Q_ASSERT(range.end().line() < lines()); //Easier for single line ranges which should happen most of the time if(range.onSingleLine()) return QString( line( range.start().line() ).mid( range.start().column(), range.columnWidth() ) ); //Add up al the requested lines QString rangedText = line(range.start().line()).mid(range.start().column()); for(int i = range.start().line() + 1; i <= range.end().line(); ++i) rangedText += '\n' + ((i == range.end().line()) ? line(i).left(range.end().column()) : line(i)); return rangedText; } static void grepLine(const QString& identifier, const QString& lineText, int lineNumber, QVector& ret, bool surroundedByBoundary) { + if (identifier.isEmpty()) + return; + int pos = 0; while(true) { pos = lineText.indexOf(identifier, pos); if(pos == -1) break; int start = pos; pos += identifier.length(); int end = pos; if(!surroundedByBoundary || ( (end == lineText.length() || !lineText[end].isLetterOrNumber() || lineText[end] != '_') && (start-1 < 0 || !lineText[start-1].isLetterOrNumber() || lineText[start-1] != '_')) ) { ret << SimpleRange(lineNumber, start, lineNumber, end); } } } class EditorCodeRepresentation : public DynamicCodeRepresentation { public: EditorCodeRepresentation(KTextEditor::Document* document) : m_document(document) { m_url = IndexedString(m_document->url()); } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < m_document->lines(); ++line) grepLine(identifier, m_document->line(line), line, ret, surroundedByBoundary); + return ret; } QString line(int line) const { if(line < 0 || line >= m_document->lines()) return QString(); return m_document->line(line); } virtual int lines() const { return m_document->lines(); } QString text() const { return m_document->text(); } bool setText(QString text) { bool ret = m_document->setText(text); ModificationRevision::clearModificationCache(m_url); return ret; } bool fileExists(){ return QFile(m_document->url().path()).exists(); } void startEdit() { m_document->startEditing(); } void endEdit() { m_document->endEditing(); } bool replace(const KTextEditor::Range& range, QString oldText, QString newText, bool ignoreOldText) { QString old = m_document->text(range); if(oldText != old && !ignoreOldText) { return false; } bool ret = m_document->replaceText(range, newText); ModificationRevision::clearModificationCache(m_url); return ret; } virtual QString rangeText(KTextEditor::Range range) const { return m_document->text(range); } private: KTextEditor::Document* m_document; IndexedString m_url; }; class FileCodeRepresentation : public CodeRepresentation { public: FileCodeRepresentation(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(); } QString line(int line) const { if(line < 0 || line >= lineData.size()) return QString(); return lineData.at(line); } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < lineData.count(); ++line) grepLine(identifier, lineData.at(line), line, ret, surroundedByBoundary); + return ret; } virtual int lines() const { return lineData.count(); } QString text() const { return data; } bool setText(QString text) { Q_ASSERT(!onDiskChangesForbidden); QString localFile(m_document.toUrl().toLocalFile()); QFile file( localFile ); if ( file.open(QIODevice::WriteOnly) ) { QByteArray data = text.toLocal8Bit(); if(file.write(data) == data.size()) { ModificationRevision::clearModificationCache(m_document); return true; } } return false; } bool fileExists(){ return m_exists; } private: //We use QByteArray, because the column-numbers are measured in utf-8 IndexedString m_document; bool m_exists; QStringList lineData; QString data; }; class ArtificialStringData : public QSharedData { public: ArtificialStringData(QString data) { setData(data); } void setData(QString data) { m_data = data; m_lineData = m_data.split('\n'); } QString data() const { return m_data; } const QStringList& lines() const { return m_lineData; } private: QString m_data; QStringList m_lineData; }; class StringCodeRepresentation : public CodeRepresentation { public: StringCodeRepresentation(KSharedPtr _data) : data(_data) { Q_ASSERT(data); } QString line(int line) const { if(line < 0 || line >= data->lines().size()) return QString(); return data->lines().at(line); } virtual int lines() const { return data->lines().count(); } QString text() const { return data->data(); } bool setText(QString text) { data->setData(text); return true; } bool fileExists(){ return false; } - virtual QVector< SimpleRange > grep ( QString identifier, bool surroundedByBoundary ) const { + virtual QVector< SimpleRange > grep ( const QString& identifier, bool surroundedByBoundary ) const { QVector< SimpleRange > ret; + + if (identifier.isEmpty()) + return ret; + for(int line = 0; line < data->lines().count(); ++line) grepLine(identifier, data->lines().at(line), line, ret, surroundedByBoundary); + return ret; } private: KSharedPtr data; }; static QHash > artificialStrings; //Return the representation for the given URL if it exists, or an empty pointer otherwise KSharedPtr representationForUrl(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(); } } bool artificialCodeRepresentationExists(IndexedString url) { return !representationForUrl(url).isNull(); } CodeRepresentation::Ptr createCodeRepresentation(IndexedString url) { if(artificialCodeRepresentationExists(url)) return CodeRepresentation::Ptr(new StringCodeRepresentation(representationForUrl(url))); IDocument* document = ICore::self()->documentController()->documentForUrl(url.toUrl()); if(document && document->textDocument()) return CodeRepresentation::Ptr(new EditorCodeRepresentation(document->textDocument())); else return CodeRepresentation::Ptr(new FileCodeRepresentation(url)); } void CodeRepresentation::setDiskChangesForbidden(bool changesForbidden) { onDiskChangesForbidden = changesForbidden; } KUrl CodeRepresentation::artificialUrl(const QString & name) { KUrl url(name); url.setScheme("artificial"); url.cleanPath(); return url; } InsertArtificialCodeRepresentation::InsertArtificialCodeRepresentation(IndexedString file, 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(new ArtificialStringData(text))); } IndexedString InsertArtificialCodeRepresentation::file() { return m_file; } InsertArtificialCodeRepresentation::~InsertArtificialCodeRepresentation() { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings.remove(m_file); } void InsertArtificialCodeRepresentation::setText(QString text) { Q_ASSERT(artificialStrings.contains(m_file)); artificialStrings[m_file]->setData(text); } QString InsertArtificialCodeRepresentation::text() { Q_ASSERT(artificialStrings.contains(m_file)); return artificialStrings[m_file]->data(); } } diff --git a/language/codegen/coderepresentation.h b/language/codegen/coderepresentation.h index 2f174f03ce..450cf25b05 100644 --- a/language/codegen/coderepresentation.h +++ b/language/codegen/coderepresentation.h @@ -1,137 +1,137 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CODEREPRESENTATION_H #define CODEREPRESENTATION_H #include "../languageexport.h" #include #include class QString; namespace KTextEditor { class Range; } namespace KDevelop { class SimpleRange; class IndexedString; /** * Allows getting code-lines conveniently, either through an open editor, or from a disk-loaded file. */ class KDEVPLATFORMLANGUAGE_EXPORT CodeRepresentation : public QSharedData { public: virtual ~CodeRepresentation() { } virtual QString line(int line) const = 0; virtual int lines() const = 0; virtual QString text() const = 0; virtual QString rangeText(KTextEditor::Range range) const; /** * Search for the given identifier in the document, and returns all ranges * where it was found. * @param identifier The identifier to search for * @param surroundedByBoundary Whether only matches that are surrounded by typical word-boundaries * should be acceded. Everything except letters, numbers, and the _ character * counts as word boundary. * */ - virtual QVector grep(QString identifier, bool surroundedByBoundary = true) const = 0; + virtual QVector grep(const QString& identifier, bool surroundedByBoundary = true) const = 0; /** * Overwrites the text in the file with the new given one * * @return true on success */ virtual bool setText(QString) = 0; /** @return true if this representation represents an actual file on disk */ virtual bool fileExists() = 0; /** * Can be used for example from tests to disallow on-disk changes. When such a change is done, an assertion is triggered. * You should enable this within tests, unless you really want to work on the disk. */ static void setDiskChangesForbidden(bool changesForbidden); /** * Returns the specified name as a url for aritificial source code * suitable for code being inserted into the parser */ static KUrl artificialUrl(const QString & name); typedef KSharedPtr Ptr; }; class KDEVPLATFORMLANGUAGE_EXPORT DynamicCodeRepresentation : public CodeRepresentation { public: /** Used to group edit-history together. Call this before a bunch of replace(), and endEdit in the end. */ virtual void startEdit() = 0; virtual bool replace(const KTextEditor::Range& range, QString oldText, QString newText, bool ignoreOldText = false) = 0; /** Must be called exactly once per startEdit() */ virtual void endEdit() = 0; typedef KSharedPtr Ptr; }; /** * Creates a code-representation for the given url, that allows conveniently accessing its data. Returns zero on failure. */ KDEVPLATFORMLANGUAGE_EXPORT CodeRepresentation::Ptr createCodeRepresentation(IndexedString url); /** * @return true if an artificial code representation already exists for the specified URL */ KDEVPLATFORMLANGUAGE_EXPORT bool artificialCodeRepresentationExists(IndexedString url); /** * Allows inserting artificial source-code into the code-representation and parsing framework. * The source-code logically represents a file. * * The artificial code is automatically removed when the object is destroyed. */ class KDEVPLATFORMLANGUAGE_EXPORT InsertArtificialCodeRepresentation : public QSharedData { public: /** * Inserts an artifial source-code representation with filename @p file and the contents @p text * If @p file is not an absolute path or url, it will be made absolute using the CodeRepresentation::artifialUrl() * function, while ensuring that the name is unique. */ InsertArtificialCodeRepresentation(IndexedString file, QString text); ~InsertArtificialCodeRepresentation(); void setText(QString text); QString text(); /** * Returns the file-name for this code-representation. */ IndexedString file(); private: InsertArtificialCodeRepresentation(const InsertArtificialCodeRepresentation&); InsertArtificialCodeRepresentation& operator=(const InsertArtificialCodeRepresentation&); IndexedString m_file; }; typedef KSharedPtr InsertArtificialCodeRepresentationPointer; } #endif diff --git a/language/duchain/classfunctiondeclaration.cpp b/language/duchain/classfunctiondeclaration.cpp index 9adcd2bb44..c450a10cf9 100644 --- a/language/duchain/classfunctiondeclaration.cpp +++ b/language/duchain/classfunctiondeclaration.cpp @@ -1,221 +1,241 @@ /* This is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "classfunctiondeclaration.h" #include "ducontext.h" #include "types/functiontype.h" #include "duchainregister.h" namespace KDevelop { static Identifier& conversionIdentifier() { static Identifier conversionIdentifierObject("operator{...cast...}"); return conversionIdentifierObject; } REGISTER_DUCHAIN_ITEM(ClassFunctionDeclaration); ClassFunctionDeclaration::ClassFunctionDeclaration(const ClassFunctionDeclaration& rhs) : ClassFunctionDeclarationBase(*new ClassFunctionDeclarationData( *rhs.d_func() )) { } void ClassFunctionDeclaration::setAbstractType(AbstractType::Ptr type) { ///TODO: write testcase for typealias case which used to trigger this warning: /// typedef bool (*EventFilter)(void *message, long *result); /// in e.g. qcoreapplication.h:172 if(type && !dynamic_cast(type.unsafeData()) && type->whichType() != AbstractType::TypeAlias) { kWarning(9505) << "WARNING: Non-function type assigned to function declaration. Type is: " << type->toString() << "whichType:" << type->whichType() << "Declaration is:" << toString() << topContext()->url().str() << range().castToSimpleRange(); } ClassMemberDeclaration::setAbstractType(type); } DEFINE_LIST_MEMBER_HASH(ClassFunctionDeclarationData, m_defaultParameters, IndexedString) ClassFunctionDeclaration::ClassFunctionDeclaration(ClassFunctionDeclarationData& data) : ClassFunctionDeclarationBase(data) { } ClassFunctionDeclaration::ClassFunctionDeclaration(const RangeInRevision& range, DUContext* context) : ClassFunctionDeclarationBase(*new ClassFunctionDeclarationData, range) { d_func_dynamic()->setClassId(this); if( context ) setContext( context ); } ClassFunctionDeclaration::ClassFunctionDeclaration(ClassFunctionDeclarationData& data, const RangeInRevision& range, DUContext* context) : ClassFunctionDeclarationBase(data, range) { if( context ) setContext( context ); } Declaration* ClassFunctionDeclaration::clonePrivate() const { return new ClassFunctionDeclaration(*this); } ClassFunctionDeclaration::~ClassFunctionDeclaration() { } bool ClassFunctionDeclaration::isFunctionDeclaration() const { return true; } QString ClassFunctionDeclaration::toString() const { if( !abstractType() ) return ClassMemberDeclaration::toString(); TypePtr function = type(); if(function) { return QString("%1 %2 %3").arg(function->partToString( FunctionType::SignatureReturn )).arg(identifier().toString()).arg(function->partToString( FunctionType::SignatureArguments )); } else { QString type = abstractType() ? abstractType()->toString() : QString(""); kDebug(9505) << "A function has a bad type attached:" << type; return QString("invalid member-function %1 type %2").arg(identifier().toString()).arg(type); } } /*bool ClassFunctionDeclaration::isSimilar(KDevelop::CodeItem *other, bool strict ) const { if (!CppClassMemberType::isSimilar(other,strict)) return false; FunctionModelItem func = dynamic_cast(other); if (isConstant() != func->isConstant()) return false; if (arguments().count() != func->arguments().count()) return false; for (int i=0; itype() != arg2->type()) return false; } return true; }*/ uint setFlag(bool enable, uint flag, uint flags) { if(enable) return flags | flag; else return flags & (~flag); } bool ClassFunctionDeclaration::isAbstract() const { return d_func()->m_functionFlags & AbstractFunctionFlag; } void ClassFunctionDeclaration::setIsAbstract(bool abstract) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(abstract, AbstractFunctionFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isFinal() const { return d_func()->m_functionFlags & FinalFunctionFlag; } void ClassFunctionDeclaration::setIsFinal(bool final) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(final, FinalFunctionFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isSignal() const { return d_func()->m_functionFlags & FunctionSignalFlag; } void ClassFunctionDeclaration::setIsSignal(bool isSignal) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isSignal, FunctionSignalFlag, d_func()->m_functionFlags); } bool ClassFunctionDeclaration::isSlot() const { return d_func()->m_functionFlags & FunctionSlotFlag; } void ClassFunctionDeclaration::setIsSlot(bool isSlot) { d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isSlot, FunctionSlotFlag, d_func()->m_functionFlags); } +bool ClassFunctionDeclaration::isDeleted() const +{ + return d_func()->m_functionFlags & DeleteFunctionFlag; +} + +void ClassFunctionDeclaration::setIsDeleted(bool isDeleted) +{ + d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isDeleted, DeleteFunctionFlag, d_func()->m_functionFlags); +} + +bool ClassFunctionDeclaration::isDefaulted() const +{ + return d_func()->m_functionFlags & DefaultFunctionFlag; +} + +void ClassFunctionDeclaration::setIsDefaulted(bool isDefaulted) +{ + d_func_dynamic()->m_functionFlags = (ClassFunctionFlags)setFlag(isDefaulted, DefaultFunctionFlag, d_func()->m_functionFlags); +} + bool ClassFunctionDeclaration::isConversionFunction() const { return identifier() == conversionIdentifier(); } bool ClassFunctionDeclaration::isConstructor() const { DUContext* ctx = context(); if (ctx && ctx->type() == DUContext::Class && ctx->localScopeIdentifier().top().nameEquals(identifier())) return true; return false; } bool ClassFunctionDeclaration::isDestructor() const { DUContext* ctx = context(); QString id = identifier().toString(); return ctx && ctx->type() == DUContext::Class && id.startsWith('~') && id.mid(1) == ctx->localScopeIdentifier().top().toString(); } uint ClassFunctionDeclaration::additionalIdentity() const { if(abstractType()) return abstractType()->hash(); else return 0; } const IndexedString* ClassFunctionDeclaration::defaultParameters() const { return d_func()->m_defaultParameters(); } unsigned int ClassFunctionDeclaration::defaultParametersSize() const { return d_func()->m_defaultParametersSize(); } void ClassFunctionDeclaration::addDefaultParameter(const IndexedString& str) { d_func_dynamic()->m_defaultParametersList().append(str); } void ClassFunctionDeclaration::clearDefaultParameters() { d_func_dynamic()->m_defaultParametersList().clear(); } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/classfunctiondeclaration.h b/language/duchain/classfunctiondeclaration.h index 3bb2d6a11f..4301319172 100644 --- a/language/duchain/classfunctiondeclaration.h +++ b/language/duchain/classfunctiondeclaration.h @@ -1,128 +1,139 @@ /* This file is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CLASSFUNCTIONDECLARATION_H #define CLASSFUNCTIONDECLARATION_H #include "classmemberdeclaration.h" #include "abstractfunctiondeclaration.h" #include "classmemberdeclarationdata.h" namespace KDevelop { +///FIXME: refactor and put signal/slot/default/delete into cpp specific API? enum ClassFunctionFlags { FunctionFlagNormal = 0, FunctionSignalFlag = 1 << 1, FunctionSlotFlag = 1 << 2, AbstractFunctionFlag = 1 << 3, - FinalFunctionFlag = 1 << 4 + FinalFunctionFlag = 1 << 4, + DefaultFunctionFlag = 1 << 5, + DeleteFunctionFlag = 1 << 6 }; KDEVPLATFORMLANGUAGE_EXPORT DECLARE_LIST_MEMBER_HASH(ClassFunctionDeclarationData, m_defaultParameters, IndexedString) class KDEVPLATFORMLANGUAGE_EXPORT ClassFunctionDeclarationData : public ClassMemberDeclarationData, public AbstractFunctionDeclarationData { public: ClassFunctionDeclarationData() { initializeAppendedLists(); m_functionFlags = FunctionFlagNormal; } ClassFunctionDeclarationData( const ClassFunctionDeclarationData& rhs ) : ClassMemberDeclarationData( rhs ), AbstractFunctionDeclarationData(rhs) { initializeAppendedLists(); copyListsFrom(rhs); m_functionFlags = rhs.m_functionFlags; } ~ClassFunctionDeclarationData() { freeAppendedLists(); } ClassFunctionFlags m_functionFlags; START_APPENDED_LISTS_BASE(ClassFunctionDeclarationData, ClassMemberDeclarationData); APPENDED_LIST_FIRST(ClassFunctionDeclarationData, IndexedString, m_defaultParameters); END_APPENDED_LISTS(ClassFunctionDeclarationData, m_defaultParameters); }; /** * Represents a single variable definition in a definition-use chain. */ typedef MergeAbstractFunctionDeclaration ClassFunctionDeclarationBase; class KDEVPLATFORMLANGUAGE_EXPORT ClassFunctionDeclaration : public ClassFunctionDeclarationBase { public: ClassFunctionDeclaration(const RangeInRevision& range, DUContext* context); ClassFunctionDeclaration(ClassFunctionDeclarationData& data, const RangeInRevision& range, DUContext* context); ClassFunctionDeclaration(ClassFunctionDeclarationData& data); ~ClassFunctionDeclaration(); ///Whether this function is a signal, for example a C++ Qt signal bool isSignal() const; void setIsSignal(bool); ///Whether this function is a slot, for example a C++ Qt slot bool isSlot() const; void setIsSlot(bool); ///Whether this function is abstract bool isAbstract() const; void setIsAbstract(bool); ///Whether this function is final bool isFinal() const; void setIsFinal(bool); + + ///Whether this function is deleted + bool isDeleted() const; + void setIsDeleted(bool); + + ///Whether this function is defaulted + bool isDefaulted() const; + void setIsDefaulted(bool); virtual bool isConstructor() const; virtual bool isDestructor() const; bool isConversionFunction() const; bool isFunctionDeclaration() const; void setIsExternalDefinition(bool); virtual QString toString() const; virtual void setAbstractType(AbstractType::Ptr type); virtual Declaration* clonePrivate() const; virtual uint additionalIdentity() const; virtual const IndexedString* defaultParameters() const; virtual unsigned int defaultParametersSize() const; virtual void addDefaultParameter(const IndexedString& str); virtual void clearDefaultParameters(); enum { Identity = 14 }; protected: ClassFunctionDeclaration(const ClassFunctionDeclaration& rhs); private: DUCHAIN_DECLARE_DATA(ClassFunctionDeclaration) }; } #endif // CLASSFUNCTIONDECLARATION_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp index 95529a0d9f..6f4974e065 100644 --- a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp +++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp @@ -1,692 +1,696 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "abstractdeclarationnavigationcontext.h" #include #include #include "../functiondeclaration.h" #include "../functiondefinition.h" #include "../classfunctiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../forwarddeclaration.h" #include "../types/enumeratortype.h" #include "../types/enumerationtype.h" #include "../types/functiontype.h" #include "../duchainutils.h" #include "../types/pointertype.h" #include "../types/referencetype.h" #include "../types/typeutils.h" #include "../persistentsymboltable.h" #include #include #include #include #include namespace KDevelop { AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext( DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext) : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : 0)), previousContext), m_declaration(decl), m_fullBackwardSearch(false) { //Jump from definition to declaration if possible FunctionDefinition* definition = dynamic_cast(m_declaration.data()); if(definition && definition->declaration()) m_declaration = DeclarationPointer(definition->declaration()); } QString AbstractDeclarationNavigationContext::name() const { if(m_declaration.data()) return prettyQualifiedIdentifier(m_declaration).toString(); else return declarationName(m_declaration); } QString AbstractDeclarationNavigationContext::html(bool shorten) { clear(); m_shorten = shorten; modifyHtml() += "

" + fontSizePrefix(shorten); addExternalHtml(m_prefix); if(!m_declaration.data()) { modifyHtml() += i18n("
lost declaration
"); return currentHtml(); } if( m_previousContext ) { modifyHtml() += navigationHighlight(i18n("Back to ")); makeLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) ); modifyHtml() += "
"; } KSharedPtr doc; if( !shorten ) { doc = ICore::self()->documentationController()->documentationForDeclaration(m_declaration.data()); const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); if( function ) { htmlFunction(); } else if( m_declaration->isTypeAlias() || m_declaration->kind() == Declaration::Instance ) { if( m_declaration->isTypeAlias() ) modifyHtml() += importantHighlight("typedef "); if(m_declaration->type()) modifyHtml() += i18n("enumerator "); AbstractType::Ptr useType = m_declaration->abstractType(); if(m_declaration->isTypeAlias()) { //Do not show the own name as type of typedefs if(useType.cast()) useType = useType.cast()->type(); } eventuallyMakeTypeLinks( useType ); modifyHtml() += ' ' + nameHighlight(Qt::escape(declarationName(m_declaration))) + "
"; }else{ if( m_declaration->kind() == Declaration::Type && m_declaration->abstractType().cast() ) { htmlClass(); } if ( m_declaration->kind() == Declaration::Namespace ) { modifyHtml() += i18n("namespace %1
", nameHighlight(Qt::escape(m_declaration->qualifiedIdentifier().toString()))); } if(m_declaration->type()) { EnumerationType::Ptr enumeration = m_declaration->type(); modifyHtml() += i18n("enumeration %1
", Qt::escape(m_declaration->identifier().toString()) ); } if(m_declaration->isForwardDeclaration()) { ForwardDeclaration* forwardDec = static_cast(m_declaration.data()); Declaration* resolved = forwardDec->resolve(m_topContext.data()); if(resolved) { modifyHtml() += i18n("( resolved forward-declaration: "); makeLink(resolved->identifier().toString(), KDevelop::DeclarationPointer(resolved), NavigationAction::NavigateDeclaration ); modifyHtml() += i18n(") "); }else{ modifyHtml() += i18n("(unresolved forward-declaration) "); QualifiedIdentifier id = forwardDec->qualifiedIdentifier(); uint count; const IndexedDeclaration* decls; PersistentSymbolTable::self().declarations(id, count, decls); bool had = false; for(uint a = 0; a < count; ++a) { if(decls[a].isValid() && !decls[a].data()->isForwardDeclaration()) { modifyHtml() += "
"; makeLink(i18n("possible resolution from"), KDevelop::DeclarationPointer(decls[a].data()), NavigationAction::NavigateDeclaration); modifyHtml() += ' ' + decls[a].data()->url().str(); had = true; } } if(had) modifyHtml() += "
"; } } } }else{ AbstractType::Ptr showType = m_declaration->abstractType(); if(showType && showType.cast()) { showType = showType.cast()->returnType(); if(showType) modifyHtml() += labelHighlight(i18n("Returns: ")); }else if(showType) { modifyHtml() += labelHighlight(i18n("Type: ")); } if(showType) { eventuallyMakeTypeLinks(showType); modifyHtml() += " "; } } QualifiedIdentifier identifier = m_declaration->qualifiedIdentifier(); if( identifier.count() > 1 ) { if( m_declaration->context() && m_declaration->context()->owner() ) { Declaration* decl = m_declaration->context()->owner(); FunctionDefinition* definition = dynamic_cast(decl); if(definition && definition->declaration()) decl = definition->declaration(); if(decl->abstractType().cast()) modifyHtml() += labelHighlight(i18n("Enum: ")); else modifyHtml() += labelHighlight(i18n("Container: ")); makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration ); modifyHtml() += " "; } else { QualifiedIdentifier parent = identifier; parent.pop(); modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(Qt::escape(parent.toString())))); } } if( shorten && !m_declaration->comment().isEmpty() ) { QString comment = QString::fromUtf8(m_declaration->comment()); if( comment.length() > 60 ) { comment.truncate(60); comment += "..."; } comment.replace('\n', " "); comment.replace("
", " "); comment.replace("
", " "); modifyHtml() += commentHighlight(Qt::escape(comment)) + " "; } QString access = stringFromAccess(m_declaration); if( !access.isEmpty() ) modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(Qt::escape(access)))); ///@todo Enumerations QString detailsHtml; QStringList details = declarationDetails(m_declaration); if( !details.isEmpty() ) { bool first = true; foreach( const QString &str, details ) { if( !first ) detailsHtml += ", "; first = false; detailsHtml += propertyHighlight(str); } } QString kind = declarationKind(m_declaration); if( !kind.isEmpty() ) { if( !detailsHtml.isEmpty() ) modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(Qt::escape(kind)), detailsHtml)); else modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(Qt::escape(kind)))); } else if( !detailsHtml.isEmpty() ) { modifyHtml() += labelHighlight(i18n("Modifiers: %1 ", importantHighlight(Qt::escape(kind)))); } modifyHtml() += "
"; if(!shorten) htmlAdditionalNavigation(); if( !shorten ) { if(dynamic_cast(m_declaration.data())) modifyHtml() += labelHighlight(i18n( "Def.: " )); else modifyHtml() += labelHighlight(i18n( "Decl.: " )); makeLink( QString("%1 :%2").arg( KUrl(m_declaration->url().str()).fileName() ).arg( m_declaration->rangeInCurrentRevision().textRange().start().line()+1 ), m_declaration, NavigationAction::JumpToSource ); modifyHtml() += " "; //modifyHtml() += "
"; if(!dynamic_cast(m_declaration.data())) { if( FunctionDefinition* definition = FunctionDefinition::definition(m_declaration.data()) ) { modifyHtml() += labelHighlight(i18n( " Def.: " )); makeLink( QString("%1 :%2").arg( KUrl(definition->url().str()).fileName() ).arg( definition->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource ); } } if( FunctionDefinition* definition = dynamic_cast(m_declaration.data()) ) { if(definition->declaration()) { modifyHtml() += labelHighlight(i18n( " Decl.: " )); makeLink( QString("%1 :%2").arg( KUrl(definition->declaration()->url().str()).fileName() ).arg( definition->declaration()->rangeInCurrentRevision().textRange().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource ); } } modifyHtml() += " "; //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), "show_uses", NavigationAction(m_declaration, NavigationAction::NavigateUses)); } if( !shorten && (!m_declaration->comment().isEmpty() || doc) ) { modifyHtml() += "
"; QString comment = QString::fromUtf8(m_declaration->comment()); if(comment.isEmpty() && doc) { comment = doc->description(); if(!comment.isEmpty()) { modifyHtml() += "
" + commentHighlight(comment); } } else if(!comment.isEmpty()) { comment.replace("
", "\n"); //do not escape html newlines within the comment comment.replace("
", "\n"); comment = Qt::escape(comment); comment.replace('\n', "
"); //Replicate newlines in html modifyHtml() += commentHighlight(comment); modifyHtml() += "
"; } } if(!shorten && doc) { modifyHtml() += "
" + i18n("Show documentation for "); makeLink( prettyQualifiedIdentifier(m_declaration).toString(), m_declaration, NavigationAction::ShowDocumentation ); } //modifyHtml() += "
"; addExternalHtml(m_suffix); modifyHtml() += fontSizeSuffix(shorten) + "

"; return currentHtml(); } KDevelop::AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(KDevelop::AbstractType::Ptr type) { return type; } void AbstractDeclarationNavigationContext::htmlFunction() { const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data()); Q_ASSERT(function); const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); const FunctionType::Ptr type = m_declaration->abstractType().cast(); if( !type ) { modifyHtml() += errorHighlight("Invalid type
"); return; } if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) { // only print return type for global functions and non-ctor/dtor methods eventuallyMakeTypeLinks( type->returnType() ); } modifyHtml() += ' ' + nameHighlight(Qt::escape(prettyIdentifier(m_declaration).toString())); if( type->arguments().count() == 0 ) { modifyHtml() += "()"; } else { modifyHtml() += "( "; bool first = true; KDevelop::DUContext* argumentContext = DUChainUtils::getArgumentContext(m_declaration.data()); if(argumentContext) { int firstDefaultParam = argumentContext->localDeclarations(m_topContext.data()).count() - function->defaultParametersSize(); int currentArgNum = 0; foreach(Declaration* argument, argumentContext->localDeclarations(m_topContext.data())) { if( !first ) modifyHtml() += ", "; first = false; AbstractType::Ptr argType = argument->abstractType(); eventuallyMakeTypeLinks( argType ); modifyHtml() += ' ' + nameHighlight(Qt::escape(argument->identifier().toString())); if( currentArgNum >= firstDefaultParam ) modifyHtml() += " = " + Qt::escape(function->defaultParameters()[ currentArgNum - firstDefaultParam ].str()); ++currentArgNum; } } modifyHtml() += " )"; } modifyHtml() += "
"; } Identifier AbstractDeclarationNavigationContext::prettyIdentifier(DeclarationPointer decl) const { Identifier ret; QualifiedIdentifier q = prettyQualifiedIdentifier(decl); if(!q.isEmpty()) ret = q.last(); return ret; } QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(DeclarationPointer decl) const { if(decl) return decl->qualifiedIdentifier(); else return QualifiedIdentifier(); } void AbstractDeclarationNavigationContext::htmlAdditionalNavigation() { ///Check if the function overrides or hides another one const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data()); if(classFunDecl) { Declaration* overridden = DUChainUtils::getOverridden(m_declaration.data()); if(overridden) { modifyHtml() += i18n("Overrides a "); makeLink(i18n("function"), QString("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(overridden->context()->owner())).toString(), QString("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; }else{ //Check if this declarations hides other declarations QList decls; foreach(const DUContext::Import &import, m_declaration->context()->importedParentContexts()) if(import.context(m_topContext.data())) decls += import.context(m_topContext.data())->findDeclarations(QualifiedIdentifier(m_declaration->identifier()), CursorInRevision::invalid(), AbstractType::Ptr(), m_topContext.data(), DUContext::DontSearchInParent); uint num = 0; foreach(Declaration* decl, decls) { modifyHtml() += i18n("Hides a "); makeLink(i18n("function"), QString("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedIdentifier(DeclarationPointer(decl->context()->owner())).toString(), QString("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), KDevelop::NavigationAction::NavigateDeclaration)); modifyHtml() += "
"; ++num; } } ///Show all places where this function is overridden if(classFunDecl->isVirtual()) { Declaration* classDecl = m_declaration->context()->owner(); if(classDecl) { uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps); if(!overriders.isEmpty()) { modifyHtml() += i18n("Overridden in "); bool first = true; foreach(Declaration* overrider, overriders) { if(!first) modifyHtml() += ", "; first = false; QString name = prettyQualifiedIdentifier(DeclarationPointer(overrider->context()->owner())).toString(); makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n("More overriders possible, show all")); } } } ///Show all classes that inherit this one uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10; QList inheriters = DUChainUtils::getInheriters(m_declaration.data(), maxAllowedSteps); if(!inheriters.isEmpty()) { modifyHtml() += i18n("Inherited by "); bool first = true; foreach(Declaration* importer, inheriters) { if(!first) modifyHtml() += ", "; first = false; QString importerName = prettyQualifiedIdentifier(DeclarationPointer(importer)).toString(); makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), KDevelop::NavigationAction::NavigateDeclaration)); } modifyHtml() += "
"; } if(maxAllowedSteps == 0) createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all")); } void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(QString string) { makeLink(string, "m_fullBackwardSearch=true", NavigationAction("m_fullBackwardSearch=true")); modifyHtml() += "
"; } NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key ) { if(key == "m_fullBackwardSearch=true") { m_fullBackwardSearch = true; clear(); } return NavigationContextPointer(this); } void AbstractDeclarationNavigationContext::htmlClass() { StructureType::Ptr klass = m_declaration->abstractType().cast(); Q_ASSERT(klass); ClassDeclaration* classDecl = dynamic_cast(klass->declaration(m_topContext.data())); if(classDecl) { switch ( classDecl->classType() ) { case ClassDeclarationData::Class: modifyHtml() += "class "; break; case ClassDeclarationData::Struct: modifyHtml() += "struct "; break; case ClassDeclarationData::Union: modifyHtml() += "union "; break; case ClassDeclarationData::Interface: modifyHtml() += "interface "; break; default: modifyHtml() += " "; break; } eventuallyMakeTypeLinks( klass.cast() ); FOREACH_FUNCTION( const KDevelop::BaseClassInstance& base, classDecl->baseClasses ) { modifyHtml() += ", " + stringFromAccess(base.access) + " " + (base.virtualInheritance ? QString("virtual") : QString()) + " "; eventuallyMakeTypeLinks(base.baseClass.abstractType()); } modifyHtml() += " "; } else { /// @todo How can we get here? and should this really be a class? modifyHtml() += "class "; eventuallyMakeTypeLinks( klass.cast() ); } } void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType) { Q_ASSERT(type); Q_ASSERT(idType); if( Declaration* decl = idType->declaration(m_topContext.data()) ) { //Remove the last template-identifiers, because we create those directly QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl)); Identifier lastId = id.last(); id.pop(); lastId.clearTemplateIdentifiers(); id.push(lastId); if(decl->context() && decl->context()->owner()) { //Also create full type-links for the context around AbstractType::Ptr contextType = decl->context()->owner()->abstractType(); IdentifiedType* contextIdType = dynamic_cast(contextType.unsafeData()); if(contextIdType && !contextIdType->equals(idType)) { //Create full type information for the context if(!id.isEmpty()) id = id.mid(id.count()-1); htmlIdentifiedType(contextType, contextIdType); modifyHtml() += Qt::escape("::"); } } //We leave out the * and & reference and pointer signs, those are added to the end makeLink(id.toString() , DeclarationPointer(idType->declaration(m_topContext.data())), NavigationAction::NavigateDeclaration ); } else { kDebug() << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url().str(); modifyHtml() += typeHighlight(Qt::escape(type->toString())); } } void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type ) { type = typeToShow(type); if( !type ) { modifyHtml() += typeHighlight(Qt::escape("")); return; } AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, m_topContext.data() ); const IdentifiedType* idType = dynamic_cast( target.unsafeData() ); kDebug() << "making type-links for" << type->toString() << typeid(*type).name(); if( idType && idType->declaration(m_topContext.data()) ) { ///@todo This is C++ specific, move into subclass if(target->modifiers() & AbstractType::ConstModifier) modifyHtml() += typeHighlight("const "); htmlIdentifiedType(target, idType); //We need to exchange the target type, else template-parameters may confuse this SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr()); AbstractType::Ptr exchanged = exchangeTarget.exchange(type); if(exchanged) { QString typeSuffixString = exchanged->toString(); QRegExp suffixExp("\\&|\\*"); int suffixPos = typeSuffixString.indexOf(suffixExp); if(suffixPos != -1) modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos)); } } else { if(idType) { kDebug() << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect(); } modifyHtml() += typeHighlight(Qt::escape(type->toString())); } } DeclarationPointer AbstractDeclarationNavigationContext::declaration() const { return m_declaration; } QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access) { switch(access) { case Declaration::Private: return "private"; case Declaration::Protected: return "protected"; case Declaration::Public: return "public"; default: break; } return ""; } QString AbstractDeclarationNavigationContext::stringFromAccess(DeclarationPointer decl) { const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { return stringFromAccess(memberDecl->accessPolicy()); } return QString(); } QString AbstractDeclarationNavigationContext::declarationName( DeclarationPointer decl ) const { if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) { if( alias->identifier().isEmpty() ) return "using namespace " + alias->importIdentifier().toString(); else return "namespace " + alias->identifier().toString() + " = " + alias->importIdentifier().toString(); } if( !decl ) return i18nc("A declaration that is unknown", "Unknown"); else return prettyIdentifier(decl).toString(); } QStringList AbstractDeclarationNavigationContext::declarationDetails(DeclarationPointer decl) { QStringList details; const AbstractFunctionDeclaration* function = dynamic_cast(decl.data()); const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { if( memberDecl->isMutable() ) details << "mutable"; if( memberDecl->isRegister() ) details << "register"; if( memberDecl->isStatic() ) details << "static"; if( memberDecl->isAuto() ) details << "auto"; if( memberDecl->isExtern() ) details << "extern"; if( memberDecl->isFriend() ) details << "friend"; } if( decl->isDefinition() ) details << "definition"; if( memberDecl && memberDecl->isForwardDeclaration() ) details << "forward"; AbstractType::Ptr t(decl->abstractType()); if( t ) { if( t->modifiers() & AbstractType::ConstModifier ) details << "constant"; if( t->modifiers() & AbstractType::VolatileModifier ) details << "volatile"; } if( function ) { if( function->isInline() ) details << "inline"; if( function->isExplicit() ) details << "explicit"; if( function->isVirtual() ) details << "virtual"; const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data()); if( classFunDecl ) { if( classFunDecl->isSignal() ) details << "signal"; if( classFunDecl->isSlot() ) details << "slot"; if( classFunDecl->isConstructor() ) details << "constructor"; if( classFunDecl->isDestructor() ) details << "destructor"; if( classFunDecl->isConversionFunction() ) details << "conversion-function"; if( classFunDecl->isAbstract() ) details << "abstract"; + if ( classFunDecl->isDefaulted() ) + details << "default"; + if ( classFunDecl->isDeleted() ) + details << "delete"; } } return details; } }