diff --git a/language/CMakeLists.txt b/language/CMakeLists.txt index 1b86a5e2b..3f00bd693 100644 --- a/language/CMakeLists.txt +++ b/language/CMakeLists.txt @@ -1,293 +1,294 @@ add_definitions( -DKDE_DEFAULT_DEBUG_AREA=9505 ) set(kdevplatformlanguage_LIB_SRCS editor/modificationrevisionset.cpp editor/documentrange.cpp editor/documentrangeobject.cpp editor/documentcursor.cpp editor/documentcursorobject.cpp editor/editorintegrator.cpp editor/editorintegratorstatic.cpp editor/hashedstring.cpp editor/modificationrevision.cpp editor/editorrevisiontracker.cpp backgroundparser/backgroundparser.cpp backgroundparser/parsejob.cpp backgroundparser/parserdependencypolicy.cpp backgroundparser/documentchangetracker.cpp duchain/specializationstore.cpp duchain/codemodel.cpp duchain/duchain.cpp duchain/duchainpointer.cpp duchain/ducontext.cpp duchain/topducontext.cpp duchain/topducontextdynamicdata.cpp duchain/functiondefinition.cpp duchain/declaration.cpp duchain/classmemberdeclaration.cpp duchain/classfunctiondeclaration.cpp duchain/use.cpp duchain/smartconverter.cpp duchain/forwarddeclaration.cpp duchain/duchainbase.cpp duchain/duchainlock.cpp duchain/identifier.cpp duchain/parsingenvironment.cpp duchain/abstractfunctiondeclaration.cpp duchain/functiondeclaration.cpp duchain/stringhelpers.cpp duchain/namespacealiasdeclaration.cpp duchain/aliasdeclaration.cpp duchain/dumpdotgraph.cpp duchain/duchainobserver.cpp duchain/duchainutils.cpp duchain/declarationid.cpp duchain/definitions.cpp duchain/uses.cpp duchain/dumpchain.cpp duchain/indexedstring.cpp duchain/referencecountedindexedstring.cpp duchain/duchainregister.cpp duchain/persistentsymboltable.cpp duchain/instantiationinformation.cpp duchain/types/typesystem.cpp duchain/types/typeregister.cpp duchain/types/identifiedtype.cpp duchain/types/abstracttype.cpp duchain/types/integraltype.cpp duchain/types/functiontype.cpp duchain/types/structuretype.cpp duchain/types/pointertype.cpp duchain/types/referencetype.cpp duchain/types/delayedtype.cpp duchain/types/arraytype.cpp duchain/types/indexedtype.cpp duchain/types/enumerationtype.cpp duchain/types/constantintegraltype.cpp duchain/types/enumeratortype.cpp duchain/types/typeutils.cpp duchain/repositories/itemrepository.cpp duchain/repositories/typerepository.cpp duchain/navigation/abstractnavigationwidget.cpp duchain/navigation/abstractnavigationcontext.cpp duchain/navigation/usesnavigationcontext.cpp duchain/navigation/abstractdeclarationnavigationcontext.cpp duchain/navigation/useswidget.cpp duchain/navigation/usescollector.cpp interfaces/ilanguagesupport.cpp interfaces/quickopendataprovider.cpp interfaces/iproblem.cpp interfaces/iquickopen.cpp interfaces/editorcontext.cpp interfaces/codecontext.cpp codecompletion/codecompletion.cpp codecompletion/codecompletionworker.cpp codecompletion/codecompletionmodel.cpp codecompletion/codecompletionitem.cpp codecompletion/codecompletioncontext.cpp codecompletion/codecompletionitemgrouper.cpp codegen/createclass.cpp codegen/codegenerator.cpp codegen/documentchangeset.cpp codegen/coderepresentation.cpp codegen/overridespage.cpp util/setrepository.cpp util/navigationtooltip.cpp ) set( kdevplatformlanguage_LIB_UI codegen/ui/newclass.ui codegen/ui/overridevirtuals.ui codegen/ui/licensechooser.ui codegen/ui/outputlocation.ui ) kde4_add_ui_files( kdevplatformlanguage_LIB_SRCS ${kdevplatformlanguage_LIB_UI} ) kde4_add_library(kdevplatformlanguage SHARED ${kdevplatformlanguage_LIB_SRCS}) -target_link_libraries(kdevplatformlanguage - ${KDE4_KPARTS_LIBS} - ${KDE4_KTEXTEDITOR_LIBS} - ${KDE4_THREADWEAVER_LIBRARIES} - kdevplatforminterfaces +target_link_libraries(kdevplatformlanguage + ${KDE4_KPARTS_LIBS} + ${KDE4_KTEXTEDITOR_LIBS} + ${KDE4_THREADWEAVER_LIBRARIES} + kdevplatforminterfaces kdevplatformutil) # Need to add kdevplatforminterfaces and kdevplatformutil when those are exported targets -target_link_libraries(kdevplatformlanguage LINK_INTERFACE_LIBRARIES - ${KDE4_KTEXTEDITOR+LIBS} +target_link_libraries(kdevplatformlanguage LINK_INTERFACE_LIBRARIES + ${KDE4_KTEXTEDITOR+LIBS} ${KDE4_THREADWEAVER_LIBRARIES}) set_target_properties(kdevplatformlanguage PROPERTIES VERSION ${KDEVPLATFORM_LIB_VERSION} SOVERSION ${KDEVPLATFORM_LIB_SOVERSION}) install(TARGETS kdevplatformlanguage EXPORT KDevPlatformTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install(FILES languageexport.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language COMPONENT Devel ) install(FILES interfaces/ilanguagesupport.h interfaces/icodehighlighting.h interfaces/quickopendataprovider.h interfaces/quickopenfilter.h interfaces/iquickopen.h interfaces/iproblem.h interfaces/codecontext.h interfaces/editorcontext.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/interfaces COMPONENT Devel ) install(FILES editor/documentrange.h editor/documentrangeobject.h editor/documentcursor.h editor/documentcursorobject.h editor/editorintegrator.h editor/hashedstring.h editor/simplecursor.h editor/simplerange.h editor/modificationrevision.h editor/modificationrevisionset.h editor/editorrevisiontracker.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/editor COMPONENT Devel ) install(FILES backgroundparser/backgroundparser.h backgroundparser/parsejob.h backgroundparser/documentchangetracker.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/backgroundparser COMPONENT Devel ) install(FILES util/navigationtooltip.h util/setrepository.h util/basicsetrepository.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/util COMPONENT Devel ) install(FILES duchain/parsingenvironment.h duchain/duchain.h duchain/codemodel.h duchain/ducontext.h duchain/ducontextdata.h duchain/arrayhelpers.h duchain/topducontext.h + duchain/topducontextutils.h duchain/topducontextdata.h duchain/declaration.h duchain/declarationdata.h duchain/classmemberdeclaration.h duchain/classmemberdeclarationdata.h duchain/classfunctiondeclaration.h duchain/functiondefinition.h duchain/use.h duchain/smartconverter.h duchain/forwarddeclaration.h duchain/duchainbase.h duchain/duchainpointer.h duchain/duchainobserver.h duchain/duchainlock.h duchain/identifier.h duchain/abstractfunctiondeclaration.h duchain/functiondeclaration.h duchain/stringhelpers.h duchain/safetycounter.h duchain/namespacealiasdeclaration.h duchain/aliasdeclaration.h duchain/dumpdotgraph.h duchain/duchainutils.h duchain/dumpchain.h duchain/indexedstring.h duchain/referencecountedindexedstring.h duchain/declarationid.h duchain/appendedlist.h duchain/duchainregister.h duchain/persistentsymboltable.h duchain/instantiationinformation.h duchain/specializationstore.h duchain/persistentsetmap.h duchain/indexeditems.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/duchain COMPONENT Devel ) install(FILES duchain/types/identifiedtype.h duchain/types/typesystem.h duchain/types/typeregister.h duchain/types/typepointer.h duchain/types/typesystemdata.h duchain/types/abstracttype.h duchain/types/integraltype.h duchain/types/functiontype.h duchain/types/structuretype.h duchain/types/pointertype.h duchain/types/referencetype.h duchain/types/delayedtype.h duchain/types/arraytype.h duchain/types/indexedtype.h duchain/types/enumerationtype.h duchain/types/constantintegraltype.h duchain/types/enumeratortype.h duchain/types/alltypes.h duchain/types/typeutils.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/duchain/types COMPONENT Devel ) install(FILES duchain/builders/abstractcontextbuilder.h duchain/builders/abstractdeclarationbuilder.h duchain/builders/abstracttypebuilder.h duchain/builders/abstractusebuilder.h duchain/builders/abstractexpressionvisitor.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/duchain/builders COMPONENT Devel ) install(FILES duchain/repositories/itemrepository.h duchain/repositories/typerepository.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/duchain/repositories COMPONENT Devel ) install(FILES codecompletion/codecompletion.h codecompletion/codecompletionworker.h codecompletion/codecompletionmodel.h codecompletion/codecompletionitem.h codecompletion/codecompletioncontext.h codecompletion/codecompletionitemgrouper.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/codecompletion COMPONENT Devel ) install(FILES codegen/createclass.h codegen/overridespage.h codegen/astchangeset.h codegen/duchainchangeset.h codegen/codegenerator.h codegen/documentchangeset.h codegen/coderepresentation.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/codegen COMPONENT Devel ) install(FILES duchain/navigation/usesnavigationcontext.h duchain/navigation/abstractnavigationcontext.h duchain/navigation/abstractdeclarationnavigationcontext.h duchain/navigation/abstractnavigationwidget.h duchain/navigation/navigationaction.h duchain/navigation/useswidget.h duchain/navigation/usescollector.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/language/duchain/navigation COMPONENT Devel ) diff --git a/language/duchain/builders/abstractcontextbuilder.h b/language/duchain/builders/abstractcontextbuilder.h index 6ca170ea1..bc36749a1 100644 --- a/language/duchain/builders/abstractcontextbuilder.h +++ b/language/duchain/builders/abstractcontextbuilder.h @@ -1,828 +1,828 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Andreas Pakulat * * Copyright 2006 Roberto Raggi * * Copyright 2006-2008 Hamish Rodda * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef ABSTRACTABSTRACTCONTEXTBUILDER_H #define ABSTRACTABSTRACTCONTEXTBUILDER_H #include #include #include #include #include #include #include "../../editor/editorintegrator.h" #include "../topducontext.h" #include "../duchainpointer.h" #include "../duchainlock.h" #include "../duchain.h" #include "../ducontext.h" #include "../identifier.h" #include "../indexedstring.h" #include "../parsingenvironment.h" #include "../smartconverter.h" namespace KDevelop { /** * \short Abstract definition-use chain context builder class * * The AbstractContextBuilder is a convenience class template for creating customized * definition-use chain context builders from an AST. It simplifies: * - use of your editor integrator * - creating or modifying an existing DUContext tree * - following a DUContext tree for second and subsequent passes, if required * - opening and closing DUContext instances * - tracking which DUContext instances are still present when recompiling, and removing DUContexts which no longer exist in the source code. * * \author Hamish Rodda \ */ template class AbstractContextBuilder { public: /// Constructor. AbstractContextBuilder() : m_editor( 0 ) , m_ownsEditorIntegrator(false) , m_compilingContexts( false ) , m_recompiling( false ) , m_lastContext( 0 ) { } /// Destructor. Deletes the editor integrator, if one was created specifically for this builder only. virtual ~AbstractContextBuilder() { if (m_ownsEditorIntegrator) delete m_editor; } /** * Associates an editor integrator with this builder. * * \param editor EditorIntegrator instance to use * \param ownsEditorIntegrator set to true if this builder created the editor integrator (and should thus delete it later), * or false if the editor integrator is owned by another object. */ void setEditor(EditorIntegrator* editor, bool ownsEditorIntegrator) { m_editor = editor; m_ownsEditorIntegrator = ownsEditorIntegrator; } /** * Entry point for building a definition-use chain with this builder. * * This function determines whether we are updating a chain, or creating a new one. If we are * creating a new chain, a new TopDUContext is created and registered with DUChain. * * \param url Url of the document being parsed. * \param node AST node to start building from. * \param updateContext TopDUContext to update if a duchain was previously created for this url, otherwise pass a null pointer. * * \returns the newly created or updated TopDUContext pointer. */ virtual ReferencedTopDUContext build( const IndexedString& url, T* node, ReferencedTopDUContext updateContext = ReferencedTopDUContext(), bool useSmart = true ) { m_compilingContexts = true; m_editor->setCurrentUrl( url, useSmart ); ReferencedTopDUContext top; { DUChainWriteLocker lock( DUChain::lock() ); top = updateContext.data(); if( top && top->smartRange() ) { if( top && top->smartRange()->parentRange() ) { //somethings wrong, a top level range can't have a parent Q_ASSERT(false); } } if( top ) { // kDebug() << "re-compiling"; m_recompiling = true; if( m_compilingContexts ) { LockedSmartInterface iface = m_editor->smart(); if( iface && top->range().textRange() != iface.currentDocument()->documentRange() ) { //Happens if the context wasn't smart top->setRange( SimpleRange( iface.currentDocument()->documentRange() ) ); } } }else { // kDebug() << "compiling"; LockedSmartInterface iface = m_editor->smart(); top = newTopContext( iface.currentDocument() ? SimpleRange( iface.currentDocument()->documentRange() ) : SimpleRange( SimpleCursor( 0, 0 ), SimpleCursor( INT_MAX, INT_MAX ) ) ); top->setSmartRange( m_editor->topRange( iface, EditorIntegrator::DefinitionUseChain ), DocumentRangeObject::Own ); top->setType( DUContext::Global ); DUChain::self()->addDocumentChain( top ); } setEncountered( top ); setContextOnNode( node, top ); } supportBuild( node, top ); { LockedSmartInterface iface = m_editor->smart(); if( iface && top->range().textRange() != iface.currentDocument()->documentRange() ) { kDebug() << "WARNING: top level context has wrong size:" << top->range().textRange() << "should be:" << iface.currentDocument()->documentRange(); top->setRange( iface.currentDocument()->documentRange() ); } } { - DUChainReadLocker lock( DUChain::lock() ); + /*DUChainReadLocker lock( DUChain::lock() ); //foreach(DUContext* context, topLevelContext->childContexts()); kDebug() << "built top-level context with" << top->localDeclarations().count() << "declarations," << top->localDeclarations().count() << " Definitions and" << top->childContexts().size() << "Child-Contexts"; foreach( DUContext* contexts, top->childContexts() ) { kDebug() << "CHILD:" << contexts->scopeIdentifier( true ) << "Parent:" << ( dynamic_cast( contexts->parentContext() ) ? "top-context" : "" ); - } + }*/ } m_compilingContexts = false; return top; } protected: /** * Support another builder by tracking the current context. * @param context the context to use. Must be set when the given node has no context. When it has one attached, this parameter is not needed. */ virtual void supportBuild( T* node, DUContext* context = 0 ) { if (!context) context = contextFromNode(node); openContext( context ); //The url must be set before supportBuild is called, together with the decision //whether smart-ranges shold be created if(m_editor->currentUrl() != currentContext()->url()) m_editor->setCurrentUrl(currentContext()->url(), true); { LockedSmartInterface iface = m_editor->smart(); m_editor->setCurrentRange(iface, currentContext()->smartRange()); } startVisiting(node); closeContext(); Q_ASSERT(m_contextStack.isEmpty()); } /** * Entry point to your visitor. Reimplement and call the appropriate visit function. * * \param node AST node to visit. */ virtual void startVisiting( T* node ) = 0; /** * Associate a \a context with a given AST \a node. Once called on a \a node, the * contextFromNode() function should return this \a context when called. * * \param node AST node to associate * \param context DUContext to associate */ virtual void setContextOnNode( T* node, DUContext* context ) = 0; /** * Retrieve an associated DUContext from the given \a node. Used on second and * subsequent passes of the context builder (for supporting other builds) * * \param node AST node which was previously associated * \returns the DUContext which was previously associated */ virtual DUContext* contextFromNode( T* node ) = 0; /** * Retrieves a text range from the given nodes. * * As editor integrators have to be extended to determine ranges from AST nodes, * this function must be reimplemented to allow generic retrieving of rangs from nodes. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual KTextEditor::Range editorFindRange( T* fromNode, T* toNode ) = 0; /** * Retrieve a text range for the given nodes. This is a special function required * by c++ support as a different range may need to be retrieved depending on * whether macros are involved. It is not usually required to implement this * function separately to editorFindRange() for other languages. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual KTextEditor::Range editorFindRangeForContext( T* fromNode, T* toNode ) { return editorFindRange(fromNode, toNode); } /** * Determine the QualifiedIdentifier which corresponds to the given ast \a node. * * \param node ast node which represents an identifier * \return the qualified identifier determined from \a node */ virtual QualifiedIdentifier identifierForNode( NameT* node ) = 0; /** * Create a new DUContext from the given \a range. * * This exists so that you can create custom DUContext subclasses for your * language if you need to. * * \param range range for the new context to encompass * \returns the newly created context */ virtual DUContext* newContext(const SimpleRange& range) { return new DUContext(range, currentContext()); } /** * Create a new TopDUContext from the given \a range. * * This exists so that you can create custom TopDUContext subclasses for your * language if you need to. * * \returns the newly created context */ virtual TopDUContext* newTopContext(const SimpleRange& range, ParsingEnvironmentFile* file = 0) { return new TopDUContext(m_editor->currentUrl(), range, file); } /// Determine the currently open context. \returns the current context. inline DUContext* currentContext() const { return m_contextStack.top(); } /// Determine the last closed context. \returns the last closed context. inline DUContext* lastContext() const { return m_lastContext; } /// Clears the last closed context. inline void clearLastContext() { m_lastContext = 0; } - + inline void setLastContext(DUContext* context) { m_lastContext = context; } - + /** * Determine if we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \returns true if an existing duchain is being updated, otherwise false. */ inline bool recompiling() const { return m_recompiling; } /** * Tell the context builder whether we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \param recomp set to true if an existing duchain is being updated, otherwise false. */ inline void setRecompiling(bool recomp) { m_recompiling = recomp; } /** * Determine whether this pass will create DUContext instances. * * On the first pass of definition-use chain compiling, DUContext instances * are created to represent contexts in the source code. These contexts are * associated with their AST nodes at the time (see setContextOnNode()). * * On second and subsequent passes, the contexts already exist and thus can be * retrieved through contextFromNode(). * * \returns true if compiling contexts (ie. 1st pass), otherwise false. */ inline bool compilingContexts() const { return m_compilingContexts; } /** * Sets whether we need to create ducontexts, ie. if this is the first pass. * * \sa compilingContexts() */ inline void setCompilingContexts(bool compilingContexts) { m_compilingContexts = compilingContexts; } /** * Create child contexts for only a portion of the document at \a url. * * \param url The url of the document to parse * \param node The AST node which corresponds to the context to parse * \param parent The DUContext which encompasses the \a node. * \returns The DUContext which was reparsed, ie. \a parent. */ DUContext* buildSubContexts( const KUrl& url, T *node, DUContext* parent ) { // m_compilingContexts = true; // m_recompiling = false; { DUChainReadLocker lock( DUChain::lock() ); m_editor->setCurrentUrl( IndexedString( url.pathOrUrl() ), (bool)parent->smartRange() ); } setContextOnNode( node, parent ); { openContext( contextFromNode( node ) ); { LockedSmartInterface iface = m_editor->smart(); m_editor->setCurrentRange( iface, m_editor->topRange( iface, EditorIntegrator::DefinitionUseChain ) ); } startVisiting( node ); closeContext(); } m_compilingContexts = false; if ( contextFromNode( node ) == parent ) { kDebug() << "Error in AbstractContextBuilder::buildSubContexts(...): du-context was not replaced with new one"; DUChainWriteLocker lock( DUChain::lock() ); deleteContextOnNode( node ); } return contextFromNode( node ); } /** * Delete the DUContext which is associated with the given \a node, * and remove the association. * * \param node Node which is associated with the context to delete. */ void deleteContextOnNode( T* node ) { delete contextFromNode( node ); setContextOnNode( node, 0 ); } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, NameT* identifier = 0) { if ( m_compilingContexts ) { #ifdef DEBUG_UPDATE_MATCHING //kDebug() << "opening context with text" << editor()->tokensToStrings( rangeNode->start_token, rangeNode->end_token ); #endif DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ? identifierForNode( identifier ) : QualifiedIdentifier() ); setContextOnNode( rangeNode, ret ); return ret; } else { openContext( contextFromNode(rangeNode) ); { LockedSmartInterface iface = editor()->smart(); editor()->setCurrentRange(iface, currentContext()->smartRange()); } return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext(T* node, const KDevelop::SimpleRange& range, DUContext::ContextType type, NameT* identifier = 0) { if (m_compilingContexts) { #ifdef DEBUG_UPDATE_MATCHING kDebug() << "opening custom context"; #endif DUContext* ret = openContextInternal(range, type, identifier ? identifierForNode(identifier) : QualifiedIdentifier()); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); { LockedSmartInterface iface = editor()->smart(); editor()->setCurrentRange(iface, currentContext()->smartRange()); } return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. * \param identifier The identifier for this context * \returns the opened context. */ DUContext* openContext(T* node, const KDevelop::SimpleRange& range, DUContext::ContextType type, QualifiedIdentifier id) { if (m_compilingContexts) { #ifdef DEBUG_UPDATE_MATCHING kDebug() << "opening custom context"; #endif DUContext* ret = openContextInternal(range, type, id); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); { LockedSmartInterface iface = editor()->smart(); editor()->setCurrentRange(iface, currentContext()->smartRange()); } return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { if ( m_compilingContexts ) { #ifdef DEBUG_UPDATE_MATCHING //kDebug() << "opening context with text" << editor()->tokensToStrings( rangeNode->start_token, rangeNode->end_token ); #endif DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ); setContextOnNode( rangeNode, ret ); return ret; } else { //kDebug() << "Opening Context associated with node"; openContext( contextFromNode(rangeNode) ); { LockedSmartInterface iface = editor()->smart(); editor()->setCurrentRange(iface, currentContext()->smartRange()); } return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param fromRange The range which starts the context. * \param toRange The range which ends the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* fromRange, T* toRange, DUContext::ContextType type, const QualifiedIdentifier& identifier = QualifiedIdentifier() ) { if ( m_compilingContexts ) { #ifdef DEBUG_UPDATE_MATCHING //kDebug() << "opening context with text" << editor()->tokensToStrings( fromRange->start_token, toRange->end_token ); #endif DUContext* ret = openContextInternal( editorFindRangeForContext( fromRange, toRange ), type, identifier ); setContextOnNode( fromRange, ret ); return ret; } else { openContext( contextFromNode(fromRange) ); { LockedSmartInterface iface = editor()->smart(); editor()->setCurrentRange(iface, currentContext()->smartRange()); } return currentContext(); } } /** * Open a newly created or previously existing context. * * The open context is put on the context stack, and becomes the new * currentContext(). * * \warning When you call this, you also have to open a range! If you want to re-use * the range associated to the context, use injectContext * * \param newContext Context to open. */ virtual void openContext( DUContext* newContext ) { m_contextStack.push( newContext ); m_nextContextStack.push( 0 ); } /** * This can be used to temporarily change the current context. * \param range The range that will be used as new current range, or zero(then the range associated to the context is used) * */ void injectContext( const LockedSmartInterface& iface, DUContext* ctx, KTextEditor::SmartRange* range = 0 ) { openContext( ctx ); m_editor->setCurrentRange( iface, range ? range : ctx->smartRange() ); } /** * Use this to close the context previously injected with injectContext. * */ void closeInjectedContext(const LockedSmartInterface& iface) { m_contextStack.pop(); m_nextContextStack.pop(); if(m_editor->smart()) m_editor->exitCurrentRange(iface); } /** * Close the current DUContext. When recompiling, this function will remove any * contexts that were not encountered in this passing run. * \note The DUChain write lock is already held here. */ virtual void closeContext() { { DUChainWriteLocker lock( DUChain::lock() ); //Remove all slaves that were not encountered while parsing if(m_compilingContexts) currentContext()->cleanIfNotEncountered( m_encountered ); setEncountered( currentContext() ); m_lastContext = currentContext(); } m_contextStack.pop(); m_nextContextStack.pop(); if(LockedSmartInterface iface = m_editor->smart()) m_editor->exitCurrentRange(iface); } /** * Remember that a specific item has been encoutered while parsing. * All items that are not encountered will be deleted at some stage. * * \param item duchain item that was encountered. * */ void setEncountered( DUChainBase* item ) { m_encountered.insert( item ); } /** * Determine whether the given \a item is in the set of encountered items. * * @return true if the \a item has been encountered, otherwise false. * */ bool wasEncountered( DUChainBase* item ) { return m_encountered.contains( item ); } /** * Set the current identifier to \a id. * * \param id the new current identifier. */ void setIdentifier( const QString& id ) { m_identifier = Identifier( id ); m_qIdentifier.push( m_identifier ); } /** * Determine the current identifier. * \returns the current identifier. */ QualifiedIdentifier qualifiedIdentifier() const { return m_qIdentifier; } /** * Clears the current identifier. */ void clearQualifiedIdentifier() { m_qIdentifier.clear(); } /** * Retrieve the associated editor integrator. * * \returns the editor integrator being used by this builder. */ EditorIntegrator* editor() const { return m_editor; } /** * Retrieve the current context stack. This function is not expected * to be used often and may be phased out. * * \todo Audit whether access to the context stack is still required, and provide * replacement functionality if possible. */ const QStack& contextStack() const { return m_contextStack; } /** * Access the index of the child context which has been encountered. * * \todo further delineate the role of this function and rename / document better. * \todo make private again? */ int& nextContextIndex() { return m_nextContextStack.top(); } /** * Open a context, either creating it if it does not exist, or referencing a previously existing * context if already encountered in a previous duchain parse run (when recompiling()). * * \param range The range of the context. * \param type The type of context to create. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ virtual DUContext* openContextInternal( const SimpleRange& range, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { Q_ASSERT( m_compilingContexts ); DUContext* ret = 0L; if(range.start > range.end) kDebug() << "Bad context-range" << range.textRange(); { if ( recompiling() ) { DUChainReadLocker readLock( DUChain::lock() ); const QVector& childContexts = currentContext()->childContexts(); LockedSmartInterface iface = m_editor->smart(); // translated is now in sync with the current state of the document, with whatever changes // have occurred since the text was fetched. SimpleRange translated = m_editor->translate(iface, range); // if(iface) // kDebug() << "translated by" << (translated.start.textCursor() - range.start.textCursor()) << (translated.end.textCursor() - range.end.textCursor()) << "to revision" << iface->currentRevision(); - + int currentIndex = nextContextIndex(); int lookingAhead = 0; - + for ( ; currentIndex < childContexts.count(); ++currentIndex ) { DUContext* child = childContexts.at( currentIndex ); // if ( child->range().start > translated.end && child->smartRange() ) { // #ifdef DEBUG_UPDATE_MATCHING // kDebug() << "While searching" << identifier << translated.textRange() << "(from" << range.textRange() << ") stopping because found" << child->localScopeIdentifier() << child->range().textRange(); // #endif // break; // } //For unnamed child-ranges, we still do range-comparison, because we cannot distinguish them in other ways if ( child->type() == type && child->localScopeIdentifier() == identifier && (!identifier.isEmpty() || child->range() == translated) ) { if(child->range() != translated && child->smartRange()) { kDebug() << "range mismatch while updating context. Range:" << child->range().textRange() << "should be:" << translated.textRange(); break; } // No need to have the translated range accurate any more // Also we can't unlock after the duchain lock is unlocked iface.unlock(); // Match ret = child; readLock.unlock(); DUChainWriteLocker writeLock( DUChain::lock() ); ret->clearImportedParentContexts(); m_editor->setCurrentRange( iface, ret->smartRange() ); ++currentIndex; break; }else{ #ifdef DEBUG_UPDATE_MATCHING if(child->type() != type) kDebug() << "type mismatch" << child->type() << type; if(child->localScopeIdentifier() != identifier) kDebug() << "identifier mismatch" << child->localScopeIdentifier() << identifier; if(translated != child->range()) kDebug() << "range mismatch" << child->range().textRange() << translated.textRange(); kDebug() << "skipping range" << childContexts.at(currentIndex)->localScopeIdentifier() << childContexts.at(currentIndex)->range().textRange(); #endif if ( child->range().start > translated.end && child->smartRange() && (currentIndex+1 == childContexts.count() || (childContexts.at(currentIndex+1)->localScopeIdentifier() != identifier || childContexts.at(currentIndex+1)->type() != type)) ) { ++lookingAhead; const int maxLookahead = 5; if(lookingAhead > maxLookahead) break; //Don't move the currentIndex too far } } } if(ret) nextContextIndex() = currentIndex; //If we had a match, jump forward to that position else ++nextContextIndex(); //If we did not have a match, just increment by 1 } if ( !ret ) { DUChainWriteLocker writeLock( DUChain::lock() ); ret = newContext( SimpleRange( range ) ); { LockedSmartInterface iface = m_editor->smart(); ret->setSmartRange( m_editor->createRange( iface, range.textRange() ), DocumentRangeObject::Own ); } ret->setType( type ); if (!identifier.isEmpty()) ret->setLocalScopeIdentifier(identifier); setInSymbolTable(ret); // if( recompiling() ) // kDebug() << "created new context while recompiling for " << identifier.toString() << "(" << ret->range().textRange() << ")"; } } m_encountered.insert( ret ); openContext( ret ); return ret; } ///This function should call context->setInSymbolTable(..) with an appropriate decision. The duchain is write-locked when this is called. virtual void setInSymbolTable(DUContext* context) { if(!context->parentContext()->inSymbolTable()) { context->setInSymbolTable(false); return; } DUContext::ContextType type = context->type(); - context->setInSymbolTable(type == DUContext::Class || type == DUContext::Namespace || type == DUContext::Global || type == DUContext::Helper || type == DUContext::Enum); + context->setInSymbolTable(type == DUContext::Class || type == DUContext::Namespace || type == DUContext::Global || type == DUContext::Helper || type == DUContext::Enum); } private: Identifier m_identifier; QualifiedIdentifier m_qIdentifier; EditorIntegrator* m_editor; bool m_ownsEditorIntegrator: 1; bool m_compilingContexts : 1; bool m_recompiling : 1; QStack m_nextContextStack; DUContext* m_lastContext; //Here all valid declarations/uses/... will be collected QSet m_encountered; QStack m_contextStack; }; } #endif diff --git a/language/duchain/classmemberdeclaration.cpp b/language/duchain/classmemberdeclaration.cpp index 250711c6f..6559dd290 100644 --- a/language/duchain/classmemberdeclaration.cpp +++ b/language/duchain/classmemberdeclaration.cpp @@ -1,164 +1,217 @@ /* This is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda 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 "classmemberdeclaration.h" #include "classmemberdeclarationdata.h" #include "duchainregister.h" namespace KDevelop { ClassMemberDeclarationData::ClassMemberDeclarationData() { m_accessPolicy = Declaration::Public; m_isStatic = false; m_isAuto = false; m_isFriend = false; m_isRegister = false; m_isExtern = false; m_isMutable = false; - + m_isNative = false; + m_isSynchronized = false; + m_isStrictFP = false; + m_isAbstract = false; } -ClassMemberDeclarationData::ClassMemberDeclarationData( const ClassMemberDeclarationData& rhs ) +ClassMemberDeclarationData::ClassMemberDeclarationData( const ClassMemberDeclarationData& rhs ) : DeclarationData( rhs ) { m_accessPolicy = rhs.m_accessPolicy; m_isStatic = rhs.m_isStatic; m_isAuto = rhs.m_isAuto; m_isFriend = rhs.m_isFriend; m_isRegister = rhs.m_isRegister; m_isExtern = rhs.m_isExtern; m_isMutable = rhs.m_isMutable; + m_isNative = rhs.m_isNative; + m_isSynchronized = rhs.m_isSynchronized; + m_isStrictFP = rhs.m_isStrictFP; + m_isAbstract = rhs.m_isAbstract; } ClassMemberDeclaration::ClassMemberDeclaration(const ClassMemberDeclaration& rhs) : Declaration(*new ClassMemberDeclarationData(*rhs.d_func())) { setSmartRange(rhs.smartRange(), DocumentRangeObject::DontOwn); } REGISTER_DUCHAIN_ITEM(ClassMemberDeclaration); Declaration* ClassMemberDeclaration::clonePrivate() const { return new ClassMemberDeclaration(*this); } ClassMemberDeclaration::ClassMemberDeclaration(const SimpleRange& range, DUContext* context) : Declaration(*new ClassMemberDeclarationData, range ) { d_func_dynamic()->setClassId(this); if( context ) setContext( context ); } ClassMemberDeclaration::ClassMemberDeclaration(ClassMemberDeclarationData& dd, const SimpleRange& range ) : Declaration(dd, range) { } ClassMemberDeclaration::ClassMemberDeclaration(ClassMemberDeclarationData& dd) : Declaration(dd) { } ClassMemberDeclaration::~ClassMemberDeclaration() { } bool ClassMemberDeclaration::isStatic() const { return d_func()->m_isStatic; } void ClassMemberDeclaration::setStatic(bool isStatic) { d_func_dynamic()->m_isStatic = isStatic; } bool ClassMemberDeclaration::isAuto() const { return d_func()->m_isAuto; } void ClassMemberDeclaration::setAuto(bool isAuto) { d_func_dynamic()->m_isAuto = isAuto; } bool ClassMemberDeclaration::isFriend() const { return d_func()->m_isFriend; } void ClassMemberDeclaration::setFriend(bool isFriend) { d_func_dynamic()->m_isFriend = isFriend; } bool ClassMemberDeclaration::isRegister() const { return d_func()->m_isRegister; } void ClassMemberDeclaration::setRegister(bool isRegister) { d_func_dynamic()->m_isRegister = isRegister; } bool ClassMemberDeclaration::isExtern() const { return d_func()->m_isExtern; } void ClassMemberDeclaration::setExtern(bool isExtern) { d_func_dynamic()->m_isExtern = isExtern; } bool ClassMemberDeclaration::isMutable() const { return d_func()->m_isMutable; } void ClassMemberDeclaration::setMutable(bool isMutable) { d_func_dynamic()->m_isMutable = isMutable; } Declaration::AccessPolicy ClassMemberDeclaration::accessPolicy() const { return d_func()->m_accessPolicy; } void ClassMemberDeclaration::setAccessPolicy(Declaration::AccessPolicy accessPolicy) { d_func_dynamic()->m_accessPolicy = accessPolicy; } +bool ClassMemberDeclaration::isNative() const +{ + return d_func()->m_isNative; +} + +void ClassMemberDeclaration::setNative(bool native) +{ + d_func_dynamic()->m_isNative = native; +} + +bool ClassMemberDeclaration::isStrictFP() const +{ + return d_func()->m_isStrictFP; +} + +void ClassMemberDeclaration::setStrictFP(bool strictFP) +{ + d_func_dynamic()->m_isStrictFP = strictFP; +} + +bool ClassMemberDeclaration::isSynchronized() const +{ + return d_func()->m_isSynchronized; +} + +void ClassMemberDeclaration::setSynchronized(bool synchronized) +{ + d_func_dynamic()->m_isSynchronized = synchronized; +} + +bool ClassMemberDeclaration::isAbstract() const +{ + return d_func()->m_isAbstract; +} + +void ClassMemberDeclaration::setAbstract(bool abstract) +{ + d_func_dynamic()->m_isAbstract = abstract; +} + + void ClassMemberDeclaration::setStorageSpecifiers(StorageSpecifiers specifiers) { DUCHAIN_D_DYNAMIC(ClassMemberDeclaration); d->m_isStatic = specifiers & StaticSpecifier; d->m_isAuto = specifiers & AutoSpecifier; d->m_isFriend = specifiers & FriendSpecifier; d->m_isRegister = specifiers & RegisterSpecifier; d->m_isExtern = specifiers & ExternSpecifier; d->m_isMutable = specifiers & MutableSpecifier; + d->m_isFinal = specifiers & FinalSpecifier; + d->m_isSynchronized = specifiers & SynchronizedSpecifier; + d->m_isNative = specifiers & NativeSpecifier; + d->m_isStrictFP = specifiers & StrictFPSpecifier; + d->m_isAbstract = specifiers & AbstractSpecifier; } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/classmemberdeclaration.h b/language/duchain/classmemberdeclaration.h index 26d3e3ae3..52290ef40 100644 --- a/language/duchain/classmemberdeclaration.h +++ b/language/duchain/classmemberdeclaration.h @@ -1,91 +1,108 @@ /* This file is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda 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 CLASSMEMBERDECLARATION_H #define CLASSMEMBERDECLARATION_H #include "declaration.h" namespace KDevelop { class ClassMemberDeclarationData; /** * Represents a single class member definition in a definition-use chain. */ class KDEVPLATFORMLANGUAGE_EXPORT ClassMemberDeclaration : public Declaration { public: ClassMemberDeclaration(const ClassMemberDeclaration& rhs); ClassMemberDeclaration(const SimpleRange& range, DUContext* context); ClassMemberDeclaration(ClassMemberDeclarationData& dd); ~ClassMemberDeclaration(); AccessPolicy accessPolicy() const; void setAccessPolicy(AccessPolicy accessPolicy); enum StorageSpecifier { StaticSpecifier = 0x1 /**< indicates static member */, AutoSpecifier = 0x2 /**< indicates automatic determination of member access */, FriendSpecifier = 0x4 /**< indicates friend member */, ExternSpecifier = 0x8 /**< indicates external declaration */, RegisterSpecifier = 0x10 /**< indicates register */, - MutableSpecifier = 0x20 /**< indicates a mutable member */ + MutableSpecifier = 0x20 /**< indicates a mutable member */, + FinalSpecifier = 0x40 /**< indicates a final declaration */, + NativeSpecifier = 0x80, + SynchronizedSpecifier = 0x100, + StrictFPSpecifier = 0x200, + AbstractSpecifier = 0x400 }; Q_DECLARE_FLAGS(StorageSpecifiers, StorageSpecifier) void setStorageSpecifiers(StorageSpecifiers specifiers); bool isStatic() const; void setStatic(bool isStatic); bool isAuto() const; void setAuto(bool isAuto); bool isFriend() const; void setFriend(bool isFriend); bool isRegister() const; void setRegister(bool isRegister); bool isExtern() const; void setExtern(bool isExtern); bool isMutable() const; void setMutable(bool isMutable); + bool isNative() const; + void setNative(bool native); + + bool isSynchronized() const; + void setSynchronized(bool synchronized); + + bool isStrictFP() const; + void setStrictFP(bool strictFP); + + bool isAbstract() const; + void setAbstract(bool abstract); + enum { Identity = 9 }; - + protected: ClassMemberDeclaration(ClassMemberDeclarationData& dd, const SimpleRange& range); - + DUCHAIN_DECLARE_DATA(ClassMemberDeclaration) private: virtual Declaration* clonePrivate() const; - + }; } Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::ClassMemberDeclaration::StorageSpecifiers) #endif // CLASSMEMBERDECLARATION_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/classmemberdeclarationdata.h b/language/duchain/classmemberdeclarationdata.h index aa71e8651..402d825a1 100644 --- a/language/duchain/classmemberdeclarationdata.h +++ b/language/duchain/classmemberdeclarationdata.h @@ -1,48 +1,53 @@ /* This is part of KDevelop Copyright 2002-2005 Roberto Raggi Copyright 2006 Adam Treat Copyright 2006 Hamish Rodda 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 CLASSMEMBERDECLARATIONDATA_H #define CLASSMEMBERDECLARATIONDATA_H #include "declarationdata.h" #include "../languageexport.h" namespace KDevelop { class KDEVPLATFORMLANGUAGE_EXPORT ClassMemberDeclarationData : public DeclarationData { public: ClassMemberDeclarationData(); ClassMemberDeclarationData( const ClassMemberDeclarationData& rhs ); - + Declaration::AccessPolicy m_accessPolicy; bool m_isStatic: 1; bool m_isAuto: 1; bool m_isFriend: 1; bool m_isRegister: 1; bool m_isExtern: 1; bool m_isMutable: 1; + bool m_isFinal: 1; + bool m_isNative: 1; + bool m_isSynchronized: 1; + bool m_isStrictFP: 1; + bool m_isAbstract: 1; }; } #endif diff --git a/language/duchain/declaration.cpp b/language/duchain/declaration.cpp index b765fa75c..6bf7e73c7 100644 --- a/language/duchain/declaration.cpp +++ b/language/duchain/declaration.cpp @@ -1,753 +1,764 @@ /* This file is part of KDevelop 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 "declaration.h" #include "declarationdata.h" #include #include #include #include #include "topducontext.h" #include "topducontextdynamicdata.h" #include "use.h" #include "forwarddeclaration.h" #include "duchain.h" #include "duchainlock.h" #include "ducontextdata.h" #include "declarationid.h" #include "uses.h" #include "indexedstring.h" #include "duchainregister.h" #include "persistentsymboltable.h" #include "repositories/stringrepository.h" #include "types/identifiedtype.h" #include "types/structuretype.h" #include "functiondefinition.h" #include "codemodel.h" #include "specializationstore.h" using namespace KTextEditor; namespace KDevelop { Repositories::StringRepository commentRepository("Comment Repository"); REGISTER_DUCHAIN_ITEM(Declaration); -DeclarationData::DeclarationData() - : m_comment(0), m_isDefinition(false), m_inSymbolTable(false), - m_isTypeAlias(false), m_anonymousInContext(false) +DeclarationData::DeclarationData() + : m_comment(0), m_isDefinition(false), m_inSymbolTable(false), + m_isTypeAlias(false), m_anonymousInContext(false), m_isFinal(false) { m_kind = Declaration::Instance; } - + DeclarationData::DeclarationData( const DeclarationData& rhs ) : DUChainBaseData(rhs) { m_identifier = rhs.m_identifier; m_declaration = rhs.m_declaration; m_type = rhs.m_type; m_kind = rhs.m_kind; m_isDefinition = rhs.m_isDefinition; m_isTypeAlias = rhs.m_isTypeAlias; m_inSymbolTable = rhs.m_inSymbolTable; m_comment = rhs.m_comment; m_anonymousInContext = rhs.m_anonymousInContext; m_internalContext = rhs.m_internalContext; + m_isFinal = rhs.m_isFinal; } Declaration::Kind Declaration::kind() const { DUCHAIN_D(Declaration); return d->m_kind; } void Declaration::setKind(Kind kind) { DUCHAIN_D_DYNAMIC(Declaration); d->m_kind = kind; updateCodeModel(); } bool Declaration::inDUChain() const { DUCHAIN_D(Declaration); if( d->m_anonymousInContext ) return false; if( !context() ) return false; TopDUContext* top = topContext(); return top && top->inDuChain(); } Declaration::Declaration( const SimpleRange& range, DUContext* context ) : DUChainBase(*new DeclarationData, range) { d_func_dynamic()->setClassId(this); m_topContext = 0; m_context = 0; m_indexInTopContext = 0; if(context) setContext(context); } uint Declaration::ownIndex() const { ENSURE_CAN_READ return m_indexInTopContext; } Declaration::Declaration(const Declaration& rhs) : DUChainBase(*new DeclarationData( *rhs.d_func() )) { setSmartRange(rhs.smartRange(), DocumentRangeObject::DontOwn); m_topContext = 0; m_context = 0; m_indexInTopContext = 0; } Declaration::Declaration( DeclarationData & dd ) : DUChainBase(dd) { m_topContext = 0; m_context = 0; m_indexInTopContext = 0; } Declaration::Declaration( DeclarationData & dd, const SimpleRange& range ) : DUChainBase(dd, range) { m_topContext = 0; m_context = 0; m_indexInTopContext = 0; } Declaration::~Declaration() { uint oldOwnIndex = m_indexInTopContext; - + TopDUContext* topContext = this->topContext(); - + //Only perform the actions when the top-context isn't being deleted, or when it hasn't been stored to disk if(!topContext->deleting() || !topContext->isOnDisk()) { DUCHAIN_D_DYNAMIC(Declaration); // Inserted by the builder after construction has finished. if( d->m_internalContext.context() ) d->m_internalContext.context()->setOwner(0); - + if (d->m_inSymbolTable && !d->m_identifier.isEmpty()) { QualifiedIdentifier id = qualifiedIdentifier(); PersistentSymbolTable::self().removeDeclaration(id, this); CodeModel::self().removeItem(url(), id); } - + d->m_inSymbolTable = false; } // If the parent-context already has dynamic data, like for example any temporary context, // always delete the declaration, to not create crashes within more complex code like C++ template stuff. if (context() && !d_func()->m_anonymousInContext) { if(!topContext->deleting() || !topContext->isOnDisk() || context()->d_func()->isDynamic()) context()->m_dynamicData->removeDeclaration(this); } - + clearOwnIndex(); - + if(!topContext->deleting() || !topContext->isOnDisk()) { setContext(0); setAbstractType(AbstractType::Ptr()); } Q_ASSERT(d_func()->isDynamic() == (!topContext->deleting() || !topContext->isOnDisk() || topContext->m_dynamicData->isTemporaryDeclarationIndex(oldOwnIndex))); //DUChain::declarationChanged(this, DUChainObserver::Deletion, DUChainObserver::NotApplicable); } QByteArray Declaration::comment() const { DUCHAIN_D(Declaration); if(!d->m_comment) return 0; else return Repositories::arrayFromItem(commentRepository.itemFromIndex(d->m_comment)); } void Declaration::setComment(const QByteArray& str) { DUCHAIN_D_DYNAMIC(Declaration); if(str.isEmpty()) d->m_comment = 0; else d->m_comment = commentRepository.index(Repositories::StringRepositoryItemRequest(str, IndexedString::hashString(str, str.length()), str.length())); } void Declaration::setComment(const QString& str) { setComment(str.toUtf8()); } Identifier Declaration::identifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier.identifier(); } IndexedIdentifier Declaration::indexedIdentifier( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_identifier; } LocalIndexedDeclaration::LocalIndexedDeclaration(Declaration* decl) { if(!decl) m_declarationIndex = 0; else m_declarationIndex = decl->m_indexInTopContext; } LocalIndexedDeclaration::LocalIndexedDeclaration(uint declarationIndex) : m_declarationIndex(declarationIndex) { } Declaration* LocalIndexedDeclaration::data(TopDUContext* top) const { if(!m_declarationIndex) return 0; return top->m_dynamicData->getDeclarationForIndex(m_declarationIndex); } bool LocalIndexedDeclaration::isLoaded(TopDUContext* top) const { if(m_declarationIndex) return top->m_dynamicData->isDeclarationForIndexLoaded(m_declarationIndex); else return false; } IndexedDeclaration::IndexedDeclaration(uint topContext, uint declarationIndex) : m_topContext(topContext), m_declarationIndex(declarationIndex) { } IndexedDeclaration::IndexedDeclaration(Declaration* decl) { if(decl) { m_topContext = decl->topContext()->ownIndex(); m_declarationIndex = decl->m_indexInTopContext; }else{ m_topContext = 0; m_declarationIndex = 0; } } Declaration* IndexedDeclaration::declaration() const { if(isDummy()) return 0; // ENSURE_CHAIN_READ_LOCKED if(!m_topContext || !m_declarationIndex) return 0; TopDUContext* ctx = DUChain::self()->chainForIndex(m_topContext); if(!ctx) return 0; return ctx->m_dynamicData->getDeclarationForIndex(m_declarationIndex); } void Declaration::rebuildDynamicData(DUContext* parent, uint ownIndex) { DUChainBase::rebuildDynamicData(parent, ownIndex); - + m_context = parent; m_topContext = parent->topContext(); m_indexInTopContext = ownIndex; - + parent->m_dynamicData->addDeclarationToHash(d_func()->m_identifier.identifier(), this); } void Declaration::setIdentifier(const Identifier& identifier) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); bool wasInSymbolTable = d->m_inSymbolTable; - + setInSymbolTable(false); - + if( m_context && !d->m_anonymousInContext ) m_context->changingIdentifier( this, d->m_identifier, identifier ); d->m_identifier = identifier; - + setInSymbolTable(wasInSymbolTable); //DUChain::declarationChanged(this, DUChainObserver::Change, DUChainObserver::Identifier); } IndexedType Declaration::indexedType() const { return d_func()->m_type; } AbstractType::Ptr Declaration::abstractType( ) const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_type.type(); } void Declaration::setAbstractType(AbstractType::Ptr type) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); //if (d->m_type) //DUChain::declarationChanged(this, DUChainObserver::Removal, DUChainObserver::DataType); d->m_type = type->indexed(); updateCodeModel(); //if (d->m_type) //DUChain::declarationChanged(this, DUChainObserver::Addition, DUChainObserver::DataType); } Declaration* Declaration::specialize(uint /*specialization*/, const TopDUContext* /*topContext*/, int /*upDistance*/) { return this; } QualifiedIdentifier Declaration::qualifiedIdentifier() const { ENSURE_CAN_READ QualifiedIdentifier ret; DUContext* ctx = m_context; if(ctx) ret = ctx->scopeIdentifier(true); ret.push(d_func()->m_identifier); return ret; } // QString Declaration::mangledIdentifier() const // { // //GNU mangling specs from http://theory.uwinnipeg.ca/gnu/gcc/gxxint_15.html // // if (abstractType()) // return abstractType()->mangled(); // // // Error... // return qualifiedIdentifier().mangled(); // } DUContext * Declaration::context() const { //ENSURE_CAN_READ Commented out for performance reasons return m_context; } bool Declaration::isAnonymous() const { return d_func()->m_anonymousInContext; } void Declaration::setContext(DUContext* context, bool anonymous) { Q_ASSERT(!context || context->topContext()); ///@todo re-enable. In C++ support we need a short window to put visible declarations into template contexts if(!specialization()) { //problem: specialization() doesn't work during destructor // ENSURE_CAN_WRITE } setInSymbolTable(false); //We don't need to clear, because it's not allowed to move from one top-context into another // clearOwnIndex(); - - + + DUCHAIN_D_DYNAMIC(Declaration); if (m_context && context) { Q_ASSERT(m_context->topContext() == context->topContext()); } if (m_context) { if( !d->m_anonymousInContext ) { m_context->m_dynamicData->removeDeclaration(this); //DUChain::declarationChanged(this, DUChainObserver::Removal, DUChainObserver::Context, m_context); } } if(context) m_topContext = context->topContext(); else m_topContext = 0; - + d->m_anonymousInContext = anonymous; m_context = context; if (context) { if(!m_indexInTopContext) allocateOwnIndex(); - + if(!d->m_anonymousInContext) { context->m_dynamicData->addDeclaration(this); //DUChain::declarationChanged(this, DUChainObserver::Addition, DUChainObserver::Context, m_context); } if(context->inSymbolTable() && !anonymous) setInSymbolTable(true); } } void Declaration::clearOwnIndex() { - + if(!m_indexInTopContext) return; - + if(!context() || (!d_func()->m_anonymousInContext && !context()->isAnonymous())) { ENSURE_CAN_WRITE } - + if(m_indexInTopContext) { Q_ASSERT(m_topContext); m_topContext->m_dynamicData->clearDeclarationIndex(this); } m_indexInTopContext = 0; } void Declaration::allocateOwnIndex() { - + ///@todo Fix multithreading stuff with template instantiation, preferably using some internal mutexes // if(context() && (!context()->isAnonymous() && !d_func()->m_anonymousInContext)) { // ENSURE_CAN_WRITE // } Q_ASSERT(m_topContext); m_indexInTopContext = m_topContext->m_dynamicData->allocateDeclarationIndex(this, d_func()->m_anonymousInContext || !context() || context()->isAnonymous()); Q_ASSERT(m_indexInTopContext); - + if(!m_topContext->m_dynamicData->getDeclarationForIndex(m_indexInTopContext)) kFatal() << "Could not re-retrieve declaration" << "index:" << m_indexInTopContext; - + } const Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) const { ENSURE_CAN_READ if(isForwardDeclaration()) { const ForwardDeclaration* dec = toForwardDeclaration(); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } Declaration* Declaration::logicalDeclaration(const TopDUContext* topContext) { ENSURE_CAN_READ if(isForwardDeclaration()) { ForwardDeclaration* dec = toForwardDeclaration(); Declaration* ret = dec->resolve(topContext); if(ret) return ret; } return this; } DUContext * Declaration::logicalInternalContext(const TopDUContext* topContext) const { ENSURE_CAN_READ if(!isDefinition()) { Declaration* def = FunctionDefinition::definition(this); if( def ) return def->internalContext(); } if( d_func()->m_isTypeAlias ) { ///If this is a type-alias, return the internal context of the actual type. AbstractType::Ptr t = abstractType(); IdentifiedType* idType = dynamic_cast(t.unsafeData()); if( idType && idType->declaration(topContext) && idType->declaration(topContext) != this ) return idType->declaration(topContext)->logicalInternalContext( topContext ); } return internalContext(); } DUContext * Declaration::internalContext() const { // ENSURE_CAN_READ return d_func()->m_internalContext.context(); } void Declaration::setInternalContext(DUContext* context) { if(this->context()) { ENSURE_CAN_WRITE } DUCHAIN_D_DYNAMIC(Declaration); if( context == d->m_internalContext.context() ) return; if(!m_topContext) { //Take the top-context from the other side. We need to allocate an index, so we can safely call setOwner(..) m_topContext = context->topContext(); allocateOwnIndex(); } - + DUContext* oldInternalContext = d->m_internalContext.context(); - + d->m_internalContext = context; //Q_ASSERT( !oldInternalContext || oldInternalContext->owner() == this ); if( oldInternalContext && oldInternalContext->owner() == this ) oldInternalContext->setOwner(0); - + if( context ) context->setOwner(this); } bool Declaration::operator ==(const Declaration & other) const { ENSURE_CAN_READ return this == &other; } QString Declaration::toString() const { return QString("%3 %4").arg(abstractType() ? abstractType()->toString() : QString("")).arg(identifier().toString()); } // kate: indent-width 2; bool Declaration::isDefinition() const { ENSURE_CAN_READ DUCHAIN_D(Declaration); return d->m_isDefinition; } void Declaration::setDeclarationIsDefinition(bool dd) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(Declaration); d->m_isDefinition = dd; // if (d->m_isDefinition && definition()) { // setDefinition(0); // } } +bool Declaration::isFinal() const +{ + return d_func()->m_isFinal; +} + +void Declaration::setFinal(bool final) +{ + d_func_dynamic()->m_isFinal = final; +} + ///@todo see whether it would be useful to create an own TypeAliasDeclaration sub-class for this bool Declaration::isTypeAlias() const { DUCHAIN_D(Declaration); return d->m_isTypeAlias; } void Declaration::setIsTypeAlias(bool isTypeAlias) { DUCHAIN_D_DYNAMIC(Declaration); d->m_isTypeAlias = isTypeAlias; } uint Declaration::specialization() const { return 0; } void Declaration::activateSpecialization() { if(specialization()) { DeclarationId baseId(id()); baseId.setSpecialization(0); SpecializationStore::self().set(baseId, specialization()); } } DeclarationId Declaration::id(bool forceDirect) const { ENSURE_CAN_READ if(inSymbolTable() && !forceDirect) return DeclarationId(qualifiedIdentifier(), additionalIdentity(), specialization()); else return DeclarationId(IndexedDeclaration(const_cast(this)), specialization()); } bool Declaration::inSymbolTable() const { DUCHAIN_D(Declaration); return d->m_inSymbolTable; } CodeModelItem::Kind kindForDeclaration(Declaration* decl) { CodeModelItem::Kind kind = CodeModelItem::Unknown; - + if(decl->kind() == Declaration::Namespace) return CodeModelItem::Namespace; - + if(decl->isFunctionDeclaration()) { kind = CodeModelItem::Function; } - + if(decl->kind() == Declaration::Type && decl->type()) kind = CodeModelItem::Class; - + if(kind == CodeModelItem::Unknown && decl->kind() == Declaration::Instance) kind = CodeModelItem::Variable; - + if(decl->isForwardDeclaration()) kind = (CodeModelItem::Kind)(kind | CodeModelItem::ForwardDeclaration); - + return kind; } void Declaration::updateCodeModel() { DUCHAIN_D(Declaration); if(!d->m_identifier.isEmpty() && d->m_inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); CodeModel::self().updateItem(url(), id, kindForDeclaration(this)); } } void Declaration::setInSymbolTable(bool inSymbolTable) { DUCHAIN_D_DYNAMIC(Declaration); if(!d->m_identifier.isEmpty()) { if(!d->m_inSymbolTable && inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().addDeclaration(id, this); - + CodeModel::self().addItem(url(), id, kindForDeclaration(this)); } - + else if(d->m_inSymbolTable && !inSymbolTable) { QualifiedIdentifier id(qualifiedIdentifier()); PersistentSymbolTable::self().removeDeclaration(id, this); - + CodeModel::self().removeItem(url(), id); } } d->m_inSymbolTable = inSymbolTable; } ForwardDeclaration* Declaration::toForwardDeclaration() { return static_cast(this); } const ForwardDeclaration* Declaration::toForwardDeclaration() const { return static_cast(this); } TopDUContext * Declaration::topContext() const { return m_topContext; } Declaration* Declaration::clonePrivate() const { return new Declaration(*this); } Declaration* Declaration::clone() const { Declaration* ret = clonePrivate(); ret->d_func_dynamic()->m_inSymbolTable = false; return ret; } bool Declaration::isForwardDeclaration() const { return false; } bool Declaration::isFunctionDeclaration() const { return false; } uint Declaration::additionalIdentity() const { return 0; } bool Declaration::equalQualifiedIdentifier(const Declaration* rhs) const { ENSURE_CAN_READ DUCHAIN_D(Declaration); if(d->m_identifier != rhs->d_func()->m_identifier) return false; - + return m_context->equalScopeIdentifier(m_context); } QList Declaration::smartUses() const { Q_ASSERT(topContext()); ENSURE_CAN_READ QSet tempUses; //First, search for uses within the own context { foreach(KTextEditor::SmartRange* range, allSmartUses(topContext(), const_cast(this))) tempUses.insert(range); } KDevVarLengthArray useContexts = DUChain::uses()->uses(id()); FOREACH_ARRAY(IndexedTopDUContext indexedContext, useContexts) { if(!indexedContext.isLoaded()) continue; TopDUContext* context = indexedContext.data(); if(context) { foreach(KTextEditor::SmartRange* range, allSmartUses(context, const_cast(this))) tempUses.insert(range); } } return tempUses.toList(); } QMap > Declaration::uses() const { ENSURE_CAN_READ QMap > tempUses; //First, search for uses within the own context { QMap& ranges(tempUses[topContext()->url()]); foreach(const SimpleRange& range, allUses(topContext(), const_cast(this))) ranges[range] = true; } KDevVarLengthArray useContexts = DUChain::uses()->uses(id()); FOREACH_ARRAY(IndexedTopDUContext indexedContext, useContexts) { TopDUContext* context = indexedContext.data(); if(context) { QMap& ranges(tempUses[context->url()]); foreach(const SimpleRange& range, allUses(context, const_cast(this))) ranges[range] = true; } } QMap > ret; for(QMap >::const_iterator it = tempUses.begin(); it != tempUses.end(); ++it) { if(!(*it).isEmpty()) { QList& list(ret[it.key()]); for(QMap::const_iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) list << it2.key(); } } return ret; } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/declaration.h b/language/duchain/declaration.h index 913b9ad9e..2cc904c9c 100644 --- a/language/duchain/declaration.h +++ b/language/duchain/declaration.h @@ -1,456 +1,462 @@ /* This file is part of KDevelop 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 DECLARATION_H #define DECLARATION_H #include #include //#include #include "../editor/documentrangeobject.h" #include "identifier.h" #include "indexedstring.h" #include "types/abstracttype.h" #include "duchainbase.h" #include "topducontext.h" #include "indexeditems.h" class QByteArray; namespace KTextEditor { class SmartRange; } namespace KDevelop { class AbstractType; class DUContext; class Use; class ForwardDeclaration; class DeclarationData; class DeclarationId; class Declaration; class IndexedTopDUContext; /** * \short Represents a single declaration in a definition-use chain. * * \note A du-context can be freely edited as long as it's parent-context is zero. * In the moment the parent-context is set, the context may only be edited when it * is allowed to edited it's top-level context (@see TopLevelContext::inDUChain()) */ class KDEVPLATFORMLANGUAGE_EXPORT Declaration : public DUChainBase { public: /// Access types enum AccessPolicy { Public /**< a public declaration */, Protected /**< a protected declaration */, - Private /**< a private declaration */ + Private /**< a private declaration */, + DefaultAccess /** TypePtr type() const { return TypePtr::dynamicCast(abstractType()); } /** * Access this declaration's type. * * \note You should not compare or permanently store instances of AbstractType::Ptr. Use IndexedType instead. * \returns this declaration's type, or null if none has been assigned. */ AbstractType::Ptr abstractType() const; /** * Set this declaration's type. * \param type the type to assign. */ template void setType(TypePtr type) { setAbstractType(AbstractType::Ptr::staticCast(type)); } /** * Set this declaration's \a type. * * \param type this declaration's new type. */ virtual void setAbstractType(AbstractType::Ptr type); /** * Return an indexed form of this declaration's type. * Should be preferred, this is the fastest way, and the correct way for doing equality-comparsion. * * \returns the declaration's type. */ IndexedType indexedType() const; /** * Set this declaration's \a identifier. * * \param identifier this declaration's new identifier */ void setIdentifier(const Identifier& identifier); /** * Access this declaration's \a identifier. * * \returns this declaration's identifier. */ Identifier identifier() const; /** * Access this declaration's \a identifier. * * \return this declaration's identifier in indexed form. This is faster than identifier(), because it * equals the internal representation. Use this for example to do equality-comparison. */ IndexedIdentifier indexedIdentifier() const; /** * Determine the global qualified identifier of this declaration. * * \note This function is expensive, equalQualifiedIdentifier() is preferred if you * just want to compare equality. */ QualifiedIdentifier qualifiedIdentifier() const; /** * Compares the qualified identifier of this declaration with the other one, without needing to compute it. * This is more efficient than comparing the results of qualifiedIdentifier(). * * \param rhs declaration to compare identifiers with * \returns true if the identifiers are equal, otherwise false. */ bool equalQualifiedIdentifier(const Declaration* rhs) const; /** * Returns the kind of this declaration. @see Kind * */ Kind kind() const; /** * Set the kind. * * \param kind new kind * */ void setKind(Kind kind); /** * Returns the comment associated to this declaration in the source-code, or an invalid string if there is none. * Stored in utf-8 encoding. * */ QByteArray comment() const; /** * Sets the comment for this declaration. Should be utf-8 encoded. * */ void setComment(const QByteArray& str); /// Sets the comment for this declaration. void setComment(const QString& str); /** * Access whether this declaration is in the symbol table. * * \returns true if this declaration is in the symbol table, otherwise false. */ bool inSymbolTable() const; /** * Adds or removes this declaration to/from the symbol table. * * \param inSymbolTable true to add this declaration to the symbol table, false to remove it. */ void setInSymbolTable(bool inSymbolTable); /** * Equivalence operator. * \param other Other declaration to compare. * \returns true if the declarations are equal, otherwise false. */ bool operator==(const Declaration& other) const; /** * Determine this declaration as a string. \returns this declaration as a string. */ virtual QString toString() const; ///@todo The following two are convenience-functions. Think whether they should stay here, or be moved out. /** * Returns a list of pairs: * An url of a file, paired together with all use-ranges of this declaration in that file. * The uses are unique, no 2 uses are returend that have the same range within the same file. * * This is a non-trivial operation. * */ QMap > uses() const; /** * Collects the smart-ranges for all uses. Uses that do not have smart ranges are not represented * in the result. * */ QList smartUses() const; /** * This hash-value should differentiate between multiple different * declarations that have the same qualifiedIdentifier, but should have a different * identity, and thus own Definitions and own Uses assigned. * * Affected by function-arguments, whether this is a template-declaration, etc.. * */ virtual uint additionalIdentity() const; /** * * */ virtual uint specialization() const; /** * @see DeclarationId * @param forceDirect When this is true, the DeclarationId is force to be direct, and can be resolved without a symbol-table and top-context * */ virtual DeclarationId id(bool forceDirect = false) const; /** * Returns an index that uniquely identifies this declaration within its surrounding top-context. That index can be passed * to TopDUContext::declarationFromIndex(index) to get the declaration. * This is only valid when the declaration is not a specialization (specialization() returns 0), and if it is not anonymous in its context. * * \note for this to be valid, allocateOwnIndex() must have been called first. * \note the highest big of the index is always zero! * \returns the index of the declaration within its TopDUContext. */ uint ownIndex() const; ///Whether this declaration has been inserted anonymously into its parent-context bool isAnonymous() const; - + /** * Clear the index for this declaration in the top context that was allocated with allocateOwnIndex(). */ void clearOwnIndex(); /** * Create an index to this declaration from the topContext(). Needed to be able to retrieve ownIndex(). */ void allocateOwnIndex(); /** * Returns a clone of this declaration, with the difference that: * - context will be zero * * The declaration will not be registered anywhere, so you must care about its deletion. * * This declaration's text-range will be referenced from the clone, so the clone must not live longer than the original. * * */ Declaration* clone() const; /** * Signalized that among multiple possible specializations, this one should be used in the UI from now on. * Currently mainly used in C++ for template support. The default-implementation registers the current specialization * of this declaration to SpecializationStore if it is nonzero. */ virtual void activateSpecialization(); enum { Identity = 7 }; protected: /** * Constructor for copy constructors in subclasses. * * \param dd data to copy. * \param url document url in which this object is located. * \param range text range which this object covers. */ Declaration( DeclarationData & dd, const SimpleRange& range ); DUCHAIN_DECLARE_DATA(Declaration) private: /** * Sub-classes should implement this and should copy as much information into the clone as possible without breaking the du-chain. * Sub-classes should also implement a public copy-constructor that can be used for cloning by sub-classes. * * \note You do not have to implement this for your language if you are not going to use it(the du-chain itself does not and should not depend on it). * */ virtual Declaration* clonePrivate() const; void updateCodeModel(); void rebuildDynamicData(DUContext* parent, uint ownIndex); friend class DUContext; friend class IndexedDeclaration; friend class LocalIndexedDeclaration; friend class TopDUContextDynamicData; DUContext* m_context; TopDUContext* m_topContext; int m_indexInTopContext; }; inline uint qHash(const IndexedDeclaration& decl) { return decl.hash(); } } Q_DECLARE_METATYPE(KDevelop::IndexedDeclaration) #endif // DECLARATION_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/declarationdata.h b/language/duchain/declarationdata.h index 4b34de7e7..c4e4e5375 100644 --- a/language/duchain/declarationdata.h +++ b/language/duchain/declarationdata.h @@ -1,64 +1,65 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda 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 DECLARATION_DATA_H #define DECLARATION_DATA_H #include "duchainbase.h" #include "declaration.h" #include "declarationid.h" #include "ducontext.h" #include "topducontext.h" #include "duchainlock.h" #include "duchain.h" #include "../languageexport.h" #include "types/indexedtype.h" namespace KDevelop { class KDEVPLATFORMLANGUAGE_EXPORT DeclarationData : public DUChainBaseData { public: DeclarationData(); DeclarationData( const DeclarationData& rhs ); IndexedDUContext m_internalContext; IndexedType m_type; IndexedIdentifier m_identifier; ///@todo Eventually move this and all the definition/declaration coupling functionality somewhere else //Holds the declaration id for this definition, if this is a definition with separate declaration DeclarationId m_declaration; - + //Index in the comment repository uint m_comment; Declaration::Kind m_kind; bool m_isDefinition : 1; bool m_inSymbolTable : 1; bool m_isTypeAlias : 1; bool m_anonymousInContext : 1; //Whether the declaration was added into the parent-context anonymously + bool m_isFinal : 1; }; } #endif diff --git a/language/duchain/ducontext.cpp b/language/duchain/ducontext.cpp index 666e72d34..d215c1ade 100644 --- a/language/duchain/ducontext.cpp +++ b/language/duchain/ducontext.cpp @@ -1,1877 +1,1878 @@ /* This is part of KDevelop Copyright 2006 Hamish Rodda 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 "ducontext.h" #include #include #include #include #include #include "../editor/editorintegrator.h" #include "ducontextdata.h" #include "declaration.h" #include "duchain.h" #include "duchainlock.h" #include "use.h" #include "identifier.h" #include "topducontext.h" #include "persistentsymboltable.h" #include "aliasdeclaration.h" #include "namespacealiasdeclaration.h" #include "abstractfunctiondeclaration.h" #include "indexedstring.h" #include "duchainregister.h" #include "topducontextdynamicdata.h" #include "arrayhelpers.h" ///It is fine to use one global static mutex here using namespace KTextEditor; //Stored statically for performance-reasons #ifndef NDEBUG #define ENSURE_CAN_WRITE_(x) {if(x->inDUChain()) { ENSURE_CHAIN_WRITE_LOCKED }} #define ENSURE_CAN_READ_(x) {if(x->inDUChain()) { ENSURE_CHAIN_READ_LOCKED }} #else #define ENSURE_CAN_WRITE_(x) #define ENSURE_CAN_READ_(x) #endif namespace KDevelop { QMutex DUContextDynamicData::m_localDeclarationsMutex(QMutex::Recursive); DEFINE_LIST_MEMBER_HASH(DUContextData, m_childContexts, LocalIndexedDUContext) DEFINE_LIST_MEMBER_HASH(DUContextData, m_importers, IndexedDUContext) DEFINE_LIST_MEMBER_HASH(DUContextData, m_importedContexts, DUContext::Import) DEFINE_LIST_MEMBER_HASH(DUContextData, m_localDeclarations, LocalIndexedDeclaration) DEFINE_LIST_MEMBER_HASH(DUContextData, m_uses, Use) REGISTER_DUCHAIN_ITEM(DUContext); const Identifier globalImportIdentifier("{...import...}"); void DUContext::rebuildDynamicData(DUContext* parent, uint ownIndex) { Q_ASSERT(!parent || ownIndex); m_dynamicData->m_topContext = parent ? parent->topContext() : static_cast(this); m_dynamicData->m_indexInTopContext = ownIndex; m_dynamicData->m_parentContext = DUContextPointer(parent); m_dynamicData->m_context = this; DUChainBase::rebuildDynamicData(parent, ownIndex); } DUContextData::DUContextData() : m_inSymbolTable(false), m_anonymousInParent(false), m_propagateDeclarations(false) { initializeAppendedLists(); } DUContextData::~DUContextData() { freeAppendedLists(); } DUContextData::DUContextData(const DUContextData& rhs) : DUChainBaseData(rhs), m_inSymbolTable(rhs.m_inSymbolTable), m_anonymousInParent(rhs.m_anonymousInParent), m_propagateDeclarations(rhs.m_propagateDeclarations) { initializeAppendedLists(); copyListsFrom(rhs); m_scopeIdentifier = rhs.m_scopeIdentifier; m_contextType = rhs.m_contextType; m_owner = rhs.m_owner; } DUContextDynamicData::DUContextDynamicData(DUContext* d) : m_topContext(0), m_hasLocalDeclarationsHash(false), m_indexInTopContext(0), m_context(d), m_rangesChanged(true) { } void DUContextDynamicData::scopeIdentifier(bool includeClasses, QualifiedIdentifier& target) const { if (m_parentContext) m_parentContext->m_dynamicData->scopeIdentifier(includeClasses, target); if (includeClasses || m_context->d_func()->m_contextType != DUContext::Class) target += m_context->d_func()->m_scopeIdentifier; } bool DUContextDynamicData::importsSafeButSlow(const DUContext* context, const TopDUContext* source, ImportsHash& checked) const { if( this == context->m_dynamicData ) return true; - + if(checked.find(this) != checked.end()) return false; checked.insert(std::make_pair(this, true)); - + FOREACH_FUNCTION( const DUContext::Import& ctx, m_context->d_func()->m_importedContexts ) { DUContext* import = ctx.context(source); if(import == context || (import && import->m_dynamicData->importsSafeButSlow(context, source, checked))) return true; } return false; } bool DUContextDynamicData::imports(const DUContext* context, const TopDUContext* source, int maxDepth) const { if( this == context->m_dynamicData ) return true; - + if(maxDepth == 0) { ImportsHash checked(500); checked.set_empty_key(0); return importsSafeButSlow(context, source, checked); } FOREACH_FUNCTION( const DUContext::Import& ctx, m_context->d_func()->m_importedContexts ) { DUContext* import = ctx.context(source); if(import == context || (import && import->m_dynamicData->imports(context, source, maxDepth-1))) return true; } return false; } IndexedDUContext::IndexedDUContext(uint topContext, uint contextIndex) : m_topContext(topContext), m_contextIndex(contextIndex) { } IndexedDUContext::IndexedDUContext(DUContext* ctx) { if(ctx) { m_topContext = ctx->topContext()->ownIndex(); m_contextIndex = ctx->m_dynamicData->m_indexInTopContext; }else{ m_topContext = 0; m_contextIndex = 0; } } IndexedTopDUContext IndexedDUContext::indexedTopContext() const { if(isDummy()) return IndexedTopDUContext(); return IndexedTopDUContext(m_topContext); } LocalIndexedDUContext::LocalIndexedDUContext(uint contextIndex) : m_contextIndex(contextIndex) { } LocalIndexedDUContext::LocalIndexedDUContext(DUContext* ctx) { if(ctx) { m_contextIndex = ctx->m_dynamicData->m_indexInTopContext; }else{ m_contextIndex = 0; } } bool LocalIndexedDUContext::isLoaded(TopDUContext* top) const { if(!m_contextIndex) return false; else return top->m_dynamicData->isContextForIndexLoaded(m_contextIndex); } DUContext* LocalIndexedDUContext::data(TopDUContext* top) const { if(!m_contextIndex) return 0; else return top->m_dynamicData->getContextForIndex(m_contextIndex); } DUContext* IndexedDUContext::context() const { if(isDummy()) return 0; // ENSURE_CHAIN_READ_LOCKED if(!m_topContext) return 0; TopDUContext* ctx = DUChain::self()->chainForIndex(m_topContext); if(!ctx) return 0; if(!m_contextIndex) return ctx; return ctx->m_dynamicData->getContextForIndex(m_contextIndex); } void DUContext::synchronizeUsesFromSmart() const { DUCHAIN_D(DUContext); if(m_dynamicData->m_rangesForUses.isEmpty() || !m_dynamicData->m_rangesChanged) return; Q_ASSERT(uint(m_dynamicData->m_rangesForUses.count()) == d->m_usesSize()); for(unsigned int a = 0; a < d->m_usesSize(); a++) if(m_dynamicData->m_rangesForUses[a]) ///@todo somehow signalize the change const_cast(d->m_uses()[a]).m_range = SimpleRange(*m_dynamicData->m_rangesForUses[a]); m_dynamicData->m_rangesChanged = false; } void DUContext::synchronizeUsesToSmart() const { DUCHAIN_D(DUContext); if(m_dynamicData->m_rangesForUses.isEmpty()) return; Q_ASSERT(uint(m_dynamicData->m_rangesForUses.count()) == d->m_usesSize()); // TODO: file close race? from here KTextEditor::SmartInterface *iface = qobject_cast( smartRange()->document() ); Q_ASSERT(iface); // TODO: file close race to here QMutexLocker l(iface->smartMutex()); for(unsigned int a = 0; a < d->m_usesSize(); a++) { if(a % 10 == 0) { //Unlock the smart-lock time by time, to increase responsiveness l.unlock(); l.relock(); } if(m_dynamicData->m_rangesForUses[a]) { m_dynamicData->m_rangesForUses[a]->start() = d->m_uses()[a].m_range.start.textCursor(); m_dynamicData->m_rangesForUses[a]->end() = d->m_uses()[a].m_range.end.textCursor(); }else{ kDebug() << "bad smart-range"; } } } void DUContext::rangePositionChanged(KTextEditor::SmartRange* range) { if(range != smartRange()) m_dynamicData->m_rangesChanged = true; } void DUContext::rangeDeleted(KTextEditor::SmartRange* range) { if(range == smartRange()) { DocumentRangeObject::rangeDeleted(range); } else { range->removeWatcher(this); int index = m_dynamicData->m_rangesForUses.indexOf(range); if(index != -1) { d_func_dynamic()->m_usesList()[index].m_range = SimpleRange(*range); m_dynamicData->m_rangesForUses[index] = 0; } if(m_dynamicData->m_rangesForUses.count(0) == m_dynamicData->m_rangesForUses.size()) m_dynamicData->m_rangesForUses.clear(); } } void DUContextDynamicData::enableLocalDeclarationsHash(DUContext* ctx, const Identifier& currentIdentifier, Declaration* currentDecl) { m_hasLocalDeclarationsHash = true; FOREACH_FUNCTION(LocalIndexedDeclaration indexedDecl, ctx->d_func()->m_localDeclarations) { Declaration* decl = indexedDecl.data(m_topContext); Q_ASSERT(decl); if(currentDecl != decl) m_localDeclarationsHash.insert( decl->identifier(), DeclarationPointer(decl) ); else m_localDeclarationsHash.insert( currentIdentifier, DeclarationPointer(decl) ); } FOREACH_FUNCTION(LocalIndexedDUContext child, ctx->d_func()->m_childContexts) { DUContext* childCtx = child.data(m_topContext); Q_ASSERT(childCtx); if(childCtx->d_func()->m_propagateDeclarations) enableLocalDeclarationsHash(childCtx, currentIdentifier, currentDecl); } } void DUContextDynamicData::disableLocalDeclarationsHash() { m_hasLocalDeclarationsHash = false; m_localDeclarationsHash.clear(); } bool DUContextDynamicData::needsLocalDeclarationsHash() { ///@todo Do this again, it brings a large performance boost //For now disable the hash, until we make sure that all declarations needed for the hash are loaded first //including those in propagating sub-contexts. //Then, also make sure that we create the declaration hash after loading if needed return false; - + if(m_context->d_func()->m_localDeclarationsSize() > 15) return true; uint propagatingChildContexts = 0; FOREACH_FUNCTION(LocalIndexedDUContext child, m_context->d_func()->m_childContexts) { DUContext* childCtx = child.data(m_topContext); Q_ASSERT(childCtx); if(childCtx->d_func()->m_propagateDeclarations) ++propagatingChildContexts; } return propagatingChildContexts > 4; } void DUContextDynamicData::addDeclarationToHash(const Identifier& identifier, Declaration* declaration) { if(m_hasLocalDeclarationsHash) m_localDeclarationsHash.insert( identifier, DeclarationPointer(declaration) ); if( m_context->d_func()->m_propagateDeclarations && m_parentContext ) m_parentContext->m_dynamicData->addDeclarationToHash(identifier, declaration); if(!m_hasLocalDeclarationsHash && needsLocalDeclarationsHash()) enableLocalDeclarationsHash(m_context, identifier, declaration); } void DUContextDynamicData::removeDeclarationFromHash(const Identifier& identifier, Declaration* declaration) { if(m_hasLocalDeclarationsHash) m_localDeclarationsHash.remove( identifier, DeclarationPointer(declaration) ); if( m_context->d_func()->m_propagateDeclarations && m_parentContext ) m_parentContext->m_dynamicData->removeDeclarationFromHash(identifier, declaration); if(m_hasLocalDeclarationsHash && !needsLocalDeclarationsHash()) disableLocalDeclarationsHash(); } void DUContextDynamicData::addDeclaration( Declaration * newDeclaration ) { // The definition may not have its identifier set when it's assigned... allow dupes here, TODO catch the error elsewhere { QMutexLocker lock(&m_localDeclarationsMutex); // m_localDeclarations.append(newDeclaration); if(m_indexInTopContext < (0xffffffff/2)) { //If this context is not temporary, added declarations shouldn't be either Q_ASSERT(newDeclaration->ownIndex() < (0xffffffff/2)); } if(m_indexInTopContext > (0xffffffff/2)) { //If this context is temporary, added declarations should be as well Q_ASSERT(newDeclaration->ownIndex() > (0xffffffff/2)); } SimpleCursor start = newDeclaration->range().start; bool inserted = false; for (int i = m_context->d_func_dynamic()->m_localDeclarationsSize()-1; i >= 0; --i) { Declaration* child = m_context->d_func_dynamic()->m_localDeclarations()[i].data(m_topContext); if(!child) { kWarning() << "child declaration number" << i << "of" << m_context->d_func_dynamic()->m_localDeclarationsSize() << "is invalid"; continue; } if(child == newDeclaration) return; if (start > child->range().start) { insertToArray(m_context->d_func_dynamic()->m_localDeclarationsList(), newDeclaration, i+1); if(!m_context->d_func()->m_localDeclarations()[i+1].data(m_topContext)) kFatal() << "Inserted a not addressable declaration"; - + inserted = true; break; } } if( !inserted ) m_context->d_func_dynamic()->m_localDeclarationsList().append(newDeclaration); addDeclarationToHash(newDeclaration->identifier(), newDeclaration); } //DUChain::contextChanged(m_context, DUChainObserver::Addition, DUChainObserver::LocalDeclarations, newDeclaration); } bool DUContextDynamicData::removeDeclaration(Declaration* declaration) { QMutexLocker lock(&m_localDeclarationsMutex); if(!m_topContext->deleting()) //We can save a lot of time by just not caring about the hash while deleting removeDeclarationFromHash(declaration->identifier(), declaration); if( removeOne(m_context->d_func_dynamic()->m_localDeclarationsList(), LocalIndexedDeclaration(declaration)) ) { //DUChain::contextChanged(m_context, DUChainObserver::Removal, DUChainObserver::LocalDeclarations, declaration); return true; }else { return false; } } void DUContext::changingIdentifier( Declaration* decl, const Identifier& from, const Identifier& to ) { QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); m_dynamicData->removeDeclarationFromHash(from, decl); m_dynamicData->addDeclarationToHash(to, decl); } void DUContextDynamicData::addChildContext( DUContext * context ) { // Internal, don't need to assert a lock Q_ASSERT(!context->m_dynamicData->m_parentContext || context->m_dynamicData->m_parentContext.data()->m_dynamicData == this ); LocalIndexedDUContext indexed(context->m_dynamicData->m_indexInTopContext); if(m_indexInTopContext < (0xffffffff/2)) { //If this context is not temporary, added declarations shouldn't be either Q_ASSERT(indexed.localIndex() < (0xffffffff/2)); } if(m_indexInTopContext > (0xffffffff/2)) { //If this context is temporary, added declarations should be as well Q_ASSERT(indexed.localIndex() > (0xffffffff/2)); } - + bool inserted = false; int childCount = m_context->d_func()->m_childContextsSize(); //Optimization: In most cases while parsing, the new child-context will be added to the end, so check if it is the case. if(m_context->d_func()->m_childContextsSize() != 0) { if(m_context->d_func()->m_childContexts()[childCount-1].data(m_topContext)->range().start <= context->range().start) goto insertAtEnd; } for (int i = 0; i < childCount; ++i) { DUContext* child = m_context->d_func()->m_childContexts()[i].data(m_topContext); if (context == child) return; if (context->range().start < child->range().start) { insertToArray(m_context->d_func_dynamic()->m_childContextsList(), indexed, i); context->m_dynamicData->m_parentContext = m_context; inserted = true; break; } } insertAtEnd: if( !inserted ) { m_context->d_func_dynamic()->m_childContextsList().append(indexed); context->m_dynamicData->m_parentContext = m_context; } if(context->d_func()->m_propagateDeclarations) { QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); disableLocalDeclarationsHash(); if(needsLocalDeclarationsHash()) enableLocalDeclarationsHash(m_context); } //DUChain::contextChanged(m_context, DUChainObserver::Addition, DUChainObserver::ChildContexts, context); } bool DUContextDynamicData::removeChildContext( DUContext* context ) { // ENSURE_CAN_WRITE if( removeOne(m_context->d_func_dynamic()->m_childContextsList(), LocalIndexedDUContext(context)) ) return true; else return false; } void DUContextDynamicData::addImportedChildContext( DUContext * context ) { // ENSURE_CAN_WRITE if(arrayContains(m_context->d_func_dynamic()->m_importersList(), IndexedDUContext(context))) { kDebug(9505) << m_context->scopeIdentifier(true).toString() << "importer added multiple times:" << context->scopeIdentifier(true).toString(); return; } m_context->d_func_dynamic()->m_importersList().append(context); //DUChain::contextChanged(m_context, DUChainObserver::Addition, DUChainObserver::ImportedChildContexts, context); } //Can also be called with a context that is not in the list void DUContextDynamicData::removeImportedChildContext( DUContext * context ) { // ENSURE_CAN_WRITE removeOne(m_context->d_func_dynamic()->m_importersList(), IndexedDUContext(context)); //if( != 0 ) //DUChain::contextChanged(m_context, DUChainObserver::Removal, DUChainObserver::ImportedChildContexts, context); } int DUContext::depth() const { { if (!parentContext()) return 0; return parentContext()->depth() + 1; } } DUContext::DUContext(DUContextData& data) : DUChainBase(data), m_dynamicData(new DUContextDynamicData(this)) { } DUContext::DUContext(const SimpleRange& range, DUContext* parent, bool anonymous) : DUChainBase(*new DUContextData(), range), m_dynamicData(new DUContextDynamicData(this)) { d_func_dynamic()->setClassId(this); if(parent) m_dynamicData->m_topContext = parent->topContext(); else m_dynamicData->m_topContext = static_cast(this); d_func_dynamic()->setClassId(this); DUCHAIN_D_DYNAMIC(DUContext); d->m_contextType = Other; m_dynamicData->m_parentContext = 0; d->m_anonymousInParent = anonymous; d->m_inSymbolTable = false; if (parent) { m_dynamicData->m_indexInTopContext = parent->topContext()->m_dynamicData->allocateContextIndex(this, parent->isAnonymous() || anonymous); Q_ASSERT(m_dynamicData->m_indexInTopContext); if( !anonymous ) parent->m_dynamicData->addChildContext(this); else m_dynamicData->m_parentContext = parent; } if(parent && !anonymous && parent->inSymbolTable()) setInSymbolTable(true); } bool DUContext::isAnonymous() const { return d_func()->m_anonymousInParent || (m_dynamicData->m_parentContext && m_dynamicData->m_parentContext->isAnonymous()); } DUContext::DUContext( DUContextData& dd, const SimpleRange& range, DUContext * parent, bool anonymous ) : DUChainBase(dd, range), m_dynamicData(new DUContextDynamicData(this)) { if(parent) m_dynamicData->m_topContext = parent->topContext(); else m_dynamicData->m_topContext = static_cast(this); DUCHAIN_D_DYNAMIC(DUContext); d->m_contextType = Other; m_dynamicData->m_parentContext = 0; d->m_inSymbolTable = false; d->m_anonymousInParent = anonymous; if (parent) { m_dynamicData->m_indexInTopContext = parent->topContext()->m_dynamicData->allocateContextIndex(this, parent->isAnonymous() || anonymous); if( !anonymous ) parent->m_dynamicData->addChildContext(this); else m_dynamicData->m_parentContext = parent; } } DUContext::DUContext(DUContext& useDataFrom) : DUChainBase(useDataFrom), m_dynamicData(useDataFrom.m_dynamicData) { } DUContext::~DUContext( ) { TopDUContext* top = topContext(); if(!top->deleting() || !top->isOnDisk()) { DUCHAIN_D_DYNAMIC(DUContext); QualifiedIdentifier id(scopeIdentifier(true)); if(d->m_inSymbolTable && parentContext()) { PersistentSymbolTable::self().removeContext(id, this); } - + if(d->m_owner.declaration()) d->m_owner.declaration()->setInternalContext(0); while( d->m_importersSize() != 0 ) { if(d->m_importers()[0].data()) d->m_importers()[0].data()->removeImportedParentContext(this); else { kDebug() << "importer disappeared"; d->m_importersList().removeOne(d->m_importers()[0]); } } clearImportedParentContexts(); } deleteChildContextsRecursively(); if(!topContext()->deleting() || !topContext()->isOnDisk()) deleteUses(); else clearUseSmartRanges(); deleteLocalDeclarations(); //If the top-context is being delete, we don't need to spend time rebuilding the inner structure. //That's expensive, especially when the data is not dynamic. if(!top->deleting() || !top->isOnDisk()) { if (m_dynamicData->m_parentContext) m_dynamicData->m_parentContext->m_dynamicData->removeChildContext(this); //DUChain::contextChanged(this, DUChainObserver::Deletion, DUChainObserver::NotApplicable); } - + top->m_dynamicData->clearContextIndex(this); - + Q_ASSERT(d_func()->isDynamic() == (!top->deleting() || !top->isOnDisk() || top->m_dynamicData->isTemporaryContextIndex(m_dynamicData->m_indexInTopContext))); } QVector< DUContext * > DUContext::childContexts( ) const { ENSURE_CAN_READ QVector< DUContext * > ret; FOREACH_FUNCTION(LocalIndexedDUContext ctx, d_func()->m_childContexts) ret << ctx.data(topContext()); return ret; } Declaration* DUContext::owner() const { ENSURE_CAN_READ return d_func()->m_owner.declaration(); } void DUContext::setOwner(Declaration* owner) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); if( owner == d->m_owner.declaration() ) return; Declaration* oldOwner = d->m_owner.declaration(); d->m_owner = owner; //Q_ASSERT(!oldOwner || oldOwner->internalContext() == this); if( oldOwner && oldOwner->internalContext() == this ) oldOwner->setInternalContext(0); //The context set as internal context should always be the last opened context if( owner ) owner->setInternalContext(this); } DUContext* DUContext::parentContext( ) const { //ENSURE_CAN_READ Commented out for performance reasons return m_dynamicData->m_parentContext.data(); } void DUContext::setPropagateDeclarations(bool propagate) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); m_dynamicData->m_parentContext->m_dynamicData->disableLocalDeclarationsHash(); d->m_propagateDeclarations = propagate; if(m_dynamicData->m_parentContext->m_dynamicData->needsLocalDeclarationsHash()) m_dynamicData->m_parentContext->m_dynamicData->enableLocalDeclarationsHash(m_dynamicData->m_parentContext.data()); } bool DUContext::isPropagateDeclarations() const { return d_func()->m_propagateDeclarations; } QList DUContext::findLocalDeclarations( const Identifier& identifier, const SimpleCursor & position, const TopDUContext* topContext, const AbstractType::Ptr& dataType, SearchFlags flags ) const { ENSURE_CAN_READ DeclarationList ret; findLocalDeclarationsInternal(identifier, position.isValid() ? position : range().end, dataType, ret, topContext ? topContext : this->topContext(), flags); return arrayToList(ret); } bool contextIsChildOrEqual(const DUContext* childContext, const DUContext* context) { if(childContext == context) return true; - + if(childContext->parentContext()) return contextIsChildOrEqual(childContext->parentContext(), context); else return false; } void DUContext::findLocalDeclarationsInternal( const Identifier& identifier, const SimpleCursor & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* /*source*/, SearchFlags flags ) const { IndexedIdentifier indexedIdentifier(identifier); { QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); struct Checker { Checker(SearchFlags flags, const AbstractType::Ptr& dataType, const SimpleCursor & position, DUContext::ContextType ownType) : m_flags(flags), m_dataType(dataType), m_position(position), m_ownType(ownType) { } Declaration* check(Declaration* declaration) { if( declaration->kind() == Declaration::Alias ) { //Apply alias declarations AliasDeclaration* alias = static_cast(declaration); if(alias->aliasedDeclaration().isValid()) { declaration = alias->aliasedDeclaration().declaration(); } else { #ifndef Q_CC_MSVC kDebug() << "lost aliased declaration"; #endif } } if( declaration->kind() == Declaration::NamespaceAlias && !(m_flags & NoFiltering) ) return 0; if((m_flags & OnlyFunctions) && !declaration->isFunctionDeclaration()) return 0; if (!m_dataType || m_dataType->indexed() == declaration->abstractType()->indexed()) if (m_ownType == Class || m_ownType == Template || m_position > declaration->range().start || !m_position.isValid()) ///@todo This is C++-specific return declaration; return 0; } SearchFlags m_flags; const AbstractType::Ptr& m_dataType; const SimpleCursor& m_position; DUContext::ContextType m_ownType; }; Checker checker(flags, dataType, position, type()); if(m_dynamicData->m_hasLocalDeclarationsHash) { //Use a special hash that contains all declarations visible in this context QHash::const_iterator it = m_dynamicData->m_localDeclarationsHash.find(identifier); QHash::const_iterator end = m_dynamicData->m_localDeclarationsHash.end(); for( ; it != end && it.key() == identifier; ++it ) { Declaration* declaration = (*it).data(); if( !declaration ) { //This should never happen, but let's see kDebug(9505) << "DUContext::findLocalDeclarationsInternal: Invalid declaration in local-declaration-hash"; continue; } Declaration* checked = checker.check(declaration); if(checked) ret.append(checked); } }else if(d_func()->m_inSymbolTable && !this->localScopeIdentifier().isEmpty() && !identifier.isEmpty()) { //This context is in the symbol table, use the symbol-table to speed up the search QualifiedIdentifier id(scopeIdentifier(true) + identifier); - + TopDUContext* top = topContext(); - + uint count; const IndexedDeclaration* declarations; PersistentSymbolTable::self().declarations(id, count, declarations); for(uint a = 0; a < count; ++a) { ///@todo Eventually do efficient iteration-free filtering if(declarations[a].topContextIndex() == top->ownIndex()) { Declaration* decl = LocalIndexedDeclaration(declarations[a].localIndex()).data(top); if(decl && contextIsChildOrEqual(decl->context(), this)) { Declaration* checked = checker.check(decl); if(checked) ret.append(checked); } } } }else { //Iterate through all declarations DUContextDynamicData::VisibleDeclarationIterator it(m_dynamicData); while(it) { Declaration* declaration = *it; if(declaration->indexedIdentifier() == indexedIdentifier) { Declaration* checked = checker.check(declaration); if(checked) ret.append(checked); } ++it; } } } } bool DUContext::foundEnough( const DeclarationList& ret, SearchFlags flags ) const { if( !ret.isEmpty() && !(flags & DUContext::NoFiltering)) return true; else return false; } bool DUContext::findDeclarationsInternal( const SearchItem::PtrList & baseIdentifiers, const SimpleCursor & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags ) const { DUCHAIN_D(DUContext); if( d_func()->m_contextType != Namespace ) { //If we're in a namespace, delay all the searching into the top-context, because only that has the overview to pick the correct declarations. for(int a = 0; a < baseIdentifiers.size(); ++a) if(!baseIdentifiers[a]->isExplicitlyGlobal && baseIdentifiers[a]->next.isEmpty()) //It makes no sense searching locally for qualified identifiers findLocalDeclarationsInternal(baseIdentifiers[a]->identifier, position, dataType, ret, source, flags); if( foundEnough(ret, flags) ) return true; } ///Step 1: Apply namespace-aliases and -imports SearchItem::PtrList aliasedIdentifiers; //Because of namespace-imports and aliases, this identifier may need to be searched under multiple names applyAliases(baseIdentifiers, aliasedIdentifiers, position, false, type() != DUContext::Namespace && type() != DUContext::Global); if( d->m_importedContextsSize() != 0 ) { ///Step 2: Give identifiers that are not marked as explicitly-global to imported contexts(explicitly global ones are treatead in TopDUContext) SearchItem::PtrList nonGlobalIdentifiers; FOREACH_ARRAY( const SearchItem::Ptr& identifier, aliasedIdentifiers ) if( !identifier->isExplicitlyGlobal ) nonGlobalIdentifiers << identifier; if( !nonGlobalIdentifiers.isEmpty() ) { for(int import = d->m_importedContextsSize()-1; import >= 0; --import ) { DUContext* context = d->m_importedContexts()[import].context(source); + /// \todo This statement is really weird! Ask david... while( !context && import > 0 ) { --import; } - + if(context == this) { kDebug() << "resolved self as import:" << scopeIdentifier(true); continue; } if( !context ) break; if( position.isValid() && d->m_importedContexts()[import].position.isValid() && position < d->m_importedContexts()[import].position ) continue; if( !context->findDeclarationsInternal(nonGlobalIdentifiers, url() == context->url() ? position : context->range().end, dataType, ret, source, flags | InImportedParentContext) ) return false; } } } if( foundEnough(ret, flags) ) return true; ///Step 3: Continue search in parent-context if (!(flags & DontSearchInParent) && shouldSearchInParent(flags) && m_dynamicData->m_parentContext) { applyUpwardsAliases(aliasedIdentifiers, source); return m_dynamicData->m_parentContext->findDeclarationsInternal(aliasedIdentifiers, url() == m_dynamicData->m_parentContext->url() ? position : m_dynamicData->m_parentContext->range().end, dataType, ret, source, flags); } return true; } QList DUContext::findDeclarations( const QualifiedIdentifier & identifier, const SimpleCursor & position, const AbstractType::Ptr& dataType, const TopDUContext* topContext, SearchFlags flags) const { ENSURE_CAN_READ DeclarationList ret; SearchItem::PtrList identifiers; identifiers << SearchItem::Ptr(new SearchItem(identifier)); findDeclarationsInternal(identifiers, position.isValid() ? position : range().end, dataType, ret, topContext ? topContext : this->topContext(), flags); return arrayToList(ret); } bool DUContext::imports(const DUContext* origin, const SimpleCursor& /*position*/ ) const { ENSURE_CAN_READ return m_dynamicData->imports(origin, topContext(), 4); } void DUContext::addIndirectImport(const DUContext::Import& import) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) { if(d->m_importedContexts()[a] == import) { d->m_importedContextsList()[a].position = import.position; return; } } ///Do not sort the imported contexts by their own line-number, it makes no sense. ///Contexts added first, aka template-contexts, should stay in first place, so they are searched first. d->m_importedContextsList().append(import); } void DUContext::addImportedParentContext( DUContext * context, const SimpleCursor& position, bool anonymous, bool /*temporary*/ ) { ENSURE_CAN_WRITE if(context == this) { kDebug() << "Tried to import self"; return; } - + Import import(context, position); addIndirectImport(import); if( !anonymous && import.isBackwardMapped() ) { ENSURE_CAN_WRITE_(context) context->m_dynamicData->addImportedChildContext(this); } //DUChain::contextChanged(this, DUChainObserver::Addition, DUChainObserver::ImportedParentContexts, context); } void DUContext::removeImportedParentContext( DUContext * context ) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); - + Import import(context, SimpleCursor::invalid()); for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) { if(d->m_importedContexts()[a] == import) { removeFromArray(d->m_importedContextsList(), a); break; } } if( !context ) return; context->m_dynamicData->removeImportedChildContext(this); //DUChain::contextChanged(this, DUChainObserver::Removal, DUChainObserver::ImportedParentContexts, context); } const IndexedDUContext* DUContext::indexedImporters() const { return d_func()->m_importers(); } uint DUContext::indexedImportersSize() const { return d_func()->m_importersSize(); } QVector DUContext::importers() const { ENSURE_CAN_READ QVector ret; FOREACH_FUNCTION(IndexedDUContext ctx, d_func()->m_importers) ret << ctx.context(); return ret; } DUContext * DUContext::findContext( const SimpleCursor& position, DUContext* parent) const { ENSURE_CAN_READ if (!parent) parent = const_cast(this); FOREACH_FUNCTION(LocalIndexedDUContext context, parent->d_func()->m_childContexts) if (context.data(topContext())->range().contains(position)) { DUContext* ret = findContext(position, context.data(topContext())); if (!ret) ret = context.data(topContext()); return ret; } return 0; } bool DUContext::parentContextOf(DUContext* context) const { if (this == context) return true; FOREACH_FUNCTION(LocalIndexedDUContext child, d_func()->m_childContexts) { if (child.data(topContext())->parentContextOf(context)) return true; } return false; } QList DUContext::allLocalDeclarations(const Identifier& identifier) const { IndexedIdentifier indexedIdentifier(identifier); - + ENSURE_CAN_READ QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); QList ret; if(m_dynamicData->m_hasLocalDeclarationsHash) { QHash::const_iterator it = m_dynamicData->m_localDeclarationsHash.find(identifier); QHash::const_iterator end = m_dynamicData->m_localDeclarationsHash.end(); for( ; it != end && it.key() == identifier; ++it ) ret << (*it).data(); }else{ DUContextDynamicData::VisibleDeclarationIterator it(m_dynamicData); while(it) { Declaration* decl = *it; if(decl->indexedIdentifier() == indexedIdentifier) ret << decl; ++it; } } return ret; } QList< QPair > DUContext::allDeclarations(const SimpleCursor& position, const TopDUContext* topContext, bool searchInParents) const { ENSURE_CAN_READ QList< QPair > ret; QHash hadContexts; // Iterate back up the chain mergeDeclarationsInternal(ret, position, hadContexts, topContext ? topContext : this->topContext(), searchInParents); return ret; } QVector DUContext::localDeclarations(const TopDUContext* source) const { ENSURE_CAN_READ QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); QVector ret; FOREACH_FUNCTION(LocalIndexedDeclaration decl, d_func()->m_localDeclarations) { ret << decl.data(topContext()); Q_ASSERT(ret.back()->context() == this); ret.back()->identifier().isEmpty(); } return ret; } void DUContext::mergeDeclarationsInternal(QList< QPair >& definitions, const SimpleCursor& position, QHash& hadContexts, const TopDUContext* source, bool searchInParents, int currentDepth) const { DUCHAIN_D(DUContext); if(hadContexts.contains(this)) return; hadContexts[this] = true; if( (type() == DUContext::Namespace || type() == DUContext::Global) && currentDepth < 1000 ) currentDepth += 1000; { QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); DUContextDynamicData::VisibleDeclarationIterator it(m_dynamicData); while(it) { Declaration* decl = *it; - + if ( decl && (!position.isValid() || decl->range().start <= position) ) definitions << qMakePair(decl, currentDepth); ++it; } } for(int a = d->m_importedContextsSize()-1; a >= 0; --a) { const Import* import(&d->m_importedContexts()[a]); DUContext* context = import->context(source); while( !context && a > 0 ) { --a; import = &d->m_importedContexts()[a]; context = import->context(source); } if( !context ) break; - + if(context == this) { kDebug() << "resolved self as import:" << scopeIdentifier(true); continue; } - + if( position.isValid() && import->position.isValid() && position < import->position ) continue; context->mergeDeclarationsInternal(definitions, SimpleCursor::invalid(), hadContexts, source, false, currentDepth+1); } if (searchInParents && parentContext()) ///Only respect the position if the parent-context is not a class(@todo this is language-dependent) parentContext()->mergeDeclarationsInternal(definitions, parentContext()->type() == DUContext::Class ? parentContext()->range().end : position, hadContexts, source, true, currentDepth+1); } void DUContext::deleteLocalDeclarations() { ENSURE_CAN_WRITE KDevVarLengthArray declarations; { QMutexLocker lock(&DUContextDynamicData::m_localDeclarationsMutex); FOREACH_FUNCTION(const LocalIndexedDeclaration& decl, d_func()->m_localDeclarations) declarations.append(decl); } TopDUContext* top = topContext(); //If we are deleting something that is not stored to disk, we need to create + delete the declarations, //so their destructor unregisters from the persistent symbol table and from TopDUContextDynamicData FOREACH_ARRAY(LocalIndexedDeclaration decl, declarations) if(decl.isLoaded(top) || !top->deleting() || !top->isOnDisk()) delete decl.data(top); } void DUContext::deleteChildContextsRecursively() { ENSURE_CAN_WRITE TopDUContext* top = topContext(); - + QVector children; FOREACH_FUNCTION(LocalIndexedDUContext ctx, d_func()->m_childContexts) children << ctx; //If we are deleting a context that is already stored to disk, we don't need to load not yet loaded child-contexts. //Else we need to, so the declarations are unregistered from symbol-table and from TopDUContextDynamicData in their destructor foreach(const LocalIndexedDUContext &ctx, children) if(ctx.isLoaded(top) || !top->deleting() || !top->isOnDisk()) delete ctx.data(top); } QVector< Declaration * > DUContext::clearLocalDeclarations( ) { QVector< Declaration * > ret = localDeclarations(); foreach (Declaration* dec, ret) dec->setContext(0); return ret; } QualifiedIdentifier DUContext::scopeIdentifier(bool includeClasses) const { ENSURE_CAN_READ QualifiedIdentifier ret; m_dynamicData->scopeIdentifier(includeClasses, ret); return ret; } bool DUContext::equalScopeIdentifier(const DUContext* rhs) const { ENSURE_CAN_READ const DUContext* left = this; const DUContext* right = rhs; while(left || right) { if(!left || !right) return false; if(!(left->d_func()->m_scopeIdentifier == right->d_func()->m_scopeIdentifier)) return false; left = left->parentContext(); right = right->parentContext(); } return true; } void DUContext::setLocalScopeIdentifier(const QualifiedIdentifier & identifier) { ENSURE_CAN_WRITE //Q_ASSERT(d_func()->m_childContexts.isEmpty() && d_func()->m_localDeclarations.isEmpty()); bool wasInSymbolTable = inSymbolTable(); setInSymbolTable(false); d_func_dynamic()->m_scopeIdentifier = identifier; setInSymbolTable(wasInSymbolTable); //DUChain::contextChanged(this, DUChainObserver::Change, DUChainObserver::Identifier); } QualifiedIdentifier DUContext::localScopeIdentifier() const { //ENSURE_CAN_READ Commented out for performance reasons return d_func()->m_scopeIdentifier; } IndexedQualifiedIdentifier DUContext::indexedLocalScopeIdentifier() const { return d_func()->m_scopeIdentifier; } DUContext::ContextType DUContext::type() const { //ENSURE_CAN_READ This is disabled, because type() is called very often while searching, and it costs us performance return d_func()->m_contextType; } void DUContext::setType(ContextType type) { ENSURE_CAN_WRITE d_func_dynamic()->m_contextType = type; //DUChain::contextChanged(this, DUChainObserver::Change, DUChainObserver::ContextType); } QList DUContext::findDeclarations(const Identifier& identifier, const SimpleCursor& position, const TopDUContext* topContext, SearchFlags flags) const { ENSURE_CAN_READ DeclarationList ret; SearchItem::PtrList identifiers; identifiers << SearchItem::Ptr(new SearchItem(QualifiedIdentifier(identifier))); findDeclarationsInternal(identifiers, position.isValid() ? position : range().end, AbstractType::Ptr(), ret, topContext ? topContext : this->topContext(), flags); return arrayToList(ret); } void DUContext::deleteUse(int index) { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); removeFromArray(d->m_usesList(), index); if(!m_dynamicData->m_rangesForUses.isEmpty()) { if(m_dynamicData->m_rangesForUses[index]) { EditorIntegrator editor; editor.setCurrentUrl(url(), (bool)smartRange()); LockedSmartInterface iface = editor.smart(); if (iface) { m_dynamicData->m_rangesForUses[index]->removeWatcher(this); EditorIntegrator::releaseRange(m_dynamicData->m_rangesForUses[index]); } } m_dynamicData->m_rangesForUses.remove(index); } } QVector DUContext::takeUseRanges() { ENSURE_CAN_WRITE QVector ret = m_dynamicData->m_rangesForUses; - + foreach(KTextEditor::SmartRange* range, ret) if(range) range->removeWatcher(this); - + m_dynamicData->m_rangesForUses.clear(); return ret; } QVector DUContext::useRanges() { ENSURE_CAN_READ return m_dynamicData->m_rangesForUses; } void DUContext::deleteUses() { ENSURE_CAN_WRITE - + DUCHAIN_D_DYNAMIC(DUContext); d->m_usesList().clear(); clearUseSmartRanges(); } bool DUContext::inDUChain() const { if( d_func()->m_anonymousInParent || !m_dynamicData->m_parentContext) return false; TopDUContext* top = topContext(); return top && top->inDuChain(); } DUContext* DUContext::specialize(uint /*specialization*/, const TopDUContext* /*topContext*/, int /*upDistance*/) { return this; } SimpleCursor DUContext::importPosition(const DUContext* target) const { ENSURE_CAN_READ DUCHAIN_D(DUContext); Import import(const_cast(target), SimpleCursor::invalid()); for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) if(d->m_importedContexts()[a] == import) return d->m_importedContexts()[a].position; return SimpleCursor::invalid(); } QVector DUContext::importedParentContexts() const { ENSURE_CAN_READ QVector ret; FOREACH_FUNCTION(const DUContext::Import& import, d_func()->m_importedContexts) ret << import; return ret; } QList DUContext::findContexts(ContextType contextType, const QualifiedIdentifier& identifier, const SimpleCursor& position, const TopDUContext* top, SearchFlags flags) const { ENSURE_CAN_READ QList ret; SearchItem::PtrList identifiers; identifiers << SearchItem::Ptr(new SearchItem(identifier)); findContextsInternal(contextType, identifiers, position.isValid() ? position : range().end, ret, top ? top : topContext(), flags); return ret; } void DUContext::applyAliases(const SearchItem::PtrList& baseIdentifiers, SearchItem::PtrList& identifiers, const SimpleCursor& position, bool canBeNamespace, bool onlyImports) const { QList imports = allLocalDeclarations(globalImportIdentifier); if(imports.isEmpty() && onlyImports) { identifiers = baseIdentifiers; return; } FOREACH_ARRAY( const SearchItem::Ptr& identifier, baseIdentifiers ) { bool addUnmodified = true; if( !identifier->isExplicitlyGlobal ) { if( !imports.isEmpty() ) { //We have namespace-imports. foreach( Declaration* importDecl, imports ) { if( importDecl->range().end > position ) continue; //Search for the identifier with the import-identifier prepended Q_ASSERT(dynamic_cast(importDecl)); NamespaceAliasDeclaration* alias = static_cast(importDecl); identifiers.append( SearchItem::Ptr( new SearchItem( alias->importIdentifier(), identifier ) ) ) ; } } if( !identifier->isEmpty() && (identifier->hasNext() || canBeNamespace) ) { QList aliases = allLocalDeclarations(identifier->identifier); if(!aliases.isEmpty()) { //The first part of the identifier has been found as a namespace-alias. //In c++, we only need the first alias. However, just to be correct, follow them all for now. foreach( Declaration* aliasDecl, aliases ) { if( aliasDecl->range().end > position ) continue; if(!dynamic_cast(aliasDecl)) continue; addUnmodified = false; //The un-modified identifier can be ignored, because it will be replaced with the resolved alias NamespaceAliasDeclaration* alias = static_cast(aliasDecl); //Create an identifier where namespace-alias part is replaced with the alias target identifiers.append( SearchItem::Ptr( new SearchItem( alias->importIdentifier(), identifier->next ) ) ) ; } } } } if( addUnmodified ) identifiers.append(identifier); } } void DUContext::applyUpwardsAliases(SearchItem::PtrList& identifiers, const TopDUContext* /*source*/) const { if(type() == Namespace) { QualifiedIdentifier localId = d_func()->m_scopeIdentifier; if(localId.isEmpty()) return; //Make sure we search for the items in all namespaces of the same name, by duplicating each one with the namespace-identifier prepended. //We do this by prepending items to the current identifiers that equal the local scope identifier. SearchItem::Ptr newItem( new SearchItem(localId) ); //This will exclude explictly global identifiers newItem->addToEachNode( identifiers ); if(!newItem->next.isEmpty()) { //Prepend the full scope before newItem DUContext* parent = m_dynamicData->m_parentContext.data(); while(parent) { newItem = SearchItem::Ptr( new SearchItem(parent->d_func()->m_scopeIdentifier, newItem) ); parent = parent->m_dynamicData->m_parentContext.data(); } newItem->isExplicitlyGlobal = true; insertToArray(identifiers, newItem, 0); } } } void DUContext::findContextsInternal(ContextType contextType, const SearchItem::PtrList& baseIdentifiers, const SimpleCursor& position, QList& ret, const TopDUContext* source, SearchFlags flags) const { DUCHAIN_D(DUContext); if (contextType == type()) { FOREACH_ARRAY( const SearchItem::Ptr& identifier, baseIdentifiers ) if (identifier->match(scopeIdentifier(true)) && (!parentContext() || !identifier->isExplicitlyGlobal) ) ret.append(const_cast(this)); } ///@todo This doesn't seem quite correct: Local Contexts aren't found anywhere ///Step 1: Apply namespace-aliases and -imports SearchItem::PtrList aliasedIdentifiers; //Because of namespace-imports and aliases, this identifier may need to be searched as under multiple names applyAliases(baseIdentifiers, aliasedIdentifiers, position, contextType == Namespace, contextType != Namespace); if( d->m_importedContextsSize() != 0 ) { ///Step 2: Give identifiers that are not marked as explicitly-global to imported contexts(explicitly global ones are treatead in TopDUContext) SearchItem::PtrList nonGlobalIdentifiers; FOREACH_ARRAY( const SearchItem::Ptr& identifier, aliasedIdentifiers ) if( !identifier->isExplicitlyGlobal ) nonGlobalIdentifiers << identifier; if( !nonGlobalIdentifiers.isEmpty() ) { for(int a = d->m_importedContextsSize()-1; a >= 0; --a) { DUContext* context = d->m_importedContexts()[a].context(source); while( !context && a > 0 ) { --a; context = d->m_importedContexts()[a].context(source); } - + if(context == this) { kDebug() << "resolved self as import:" << scopeIdentifier(true); continue; } - + if( !context ) break; context->findContextsInternal(contextType, nonGlobalIdentifiers, url() == context->url() ? position : context->range().end, ret, source, flags | InImportedParentContext); } } } ///Step 3: Continue search in parent if ( !(flags & DontSearchInParent) && shouldSearchInParent(flags) && parentContext()) { applyUpwardsAliases(aliasedIdentifiers, source); parentContext()->findContextsInternal(contextType, aliasedIdentifiers, url() == parentContext()->url() ? position : parentContext()->range().end, ret, source, flags); } } bool DUContext::shouldSearchInParent(SearchFlags flags) const { return (parentContext() && parentContext()->type() == DUContext::Helper && (flags & InImportedParentContext)) || !(flags & InImportedParentContext); } const Use* DUContext::uses() const { ENSURE_CAN_READ synchronizeUsesFromSmart(); return d_func()->m_uses(); } int DUContext::usesCount() const { return d_func()->m_usesSize(); } int DUContext::createUse(int declarationIndex, const SimpleRange& range, KTextEditor::SmartRange* smartRange, int insertBefore) { DUCHAIN_D_DYNAMIC(DUContext); ENSURE_CAN_WRITE if(insertBefore == -1) { //Find position where to insert unsigned int a = 0; for(; a < d->m_usesSize() && range.start > d->m_uses()[a].m_range.start; ++a) { ///@todo do binary search } insertBefore = a; } insertToArray(d->m_usesList(), Use(range, declarationIndex), insertBefore); if(smartRange) { ///When this assertion triggers, then the updated context probably was not smart-converted before processing. @see SmartConverter Q_ASSERT(uint(m_dynamicData->m_rangesForUses.size()) == d->m_usesSize()-1); m_dynamicData->m_rangesForUses.insert(insertBefore, smartRange); smartRange->addWatcher(this); // smartRange->setWantsDirectChanges(true); d->m_usesList()[insertBefore].m_range = SimpleRange(*smartRange); }else{ // This can happen eg. when a document is closed during its parsing, and has no ill effects. //Q_ASSERT(m_dynamicData->m_rangesForUses.isEmpty()); } return insertBefore; } KTextEditor::SmartRange* DUContext::useSmartRange(int useIndex) { ENSURE_CAN_READ if(m_dynamicData->m_rangesForUses.isEmpty()) return 0; else{ if(useIndex >= 0 && useIndex < m_dynamicData->m_rangesForUses.size()) return m_dynamicData->m_rangesForUses.at(useIndex); else return 0; } } void DUContext::setUseSmartRange(int useIndex, KTextEditor::SmartRange* range) { ENSURE_CAN_WRITE if(m_dynamicData->m_rangesForUses.isEmpty()) m_dynamicData->m_rangesForUses.insert(0, d_func()->m_usesSize(), 0); Q_ASSERT(uint(m_dynamicData->m_rangesForUses.size()) == d_func()->m_usesSize()); if(m_dynamicData->m_rangesForUses[useIndex]) { EditorIntegrator editor; editor.setCurrentUrl(url(), (bool)range); LockedSmartInterface iface = editor.smart(); if (iface) { m_dynamicData->m_rangesForUses[useIndex]->removeWatcher(this); EditorIntegrator::releaseRange(m_dynamicData->m_rangesForUses[useIndex]); } } m_dynamicData->m_rangesForUses[useIndex] = range; d_func_dynamic()->m_usesList()[useIndex].m_range = SimpleRange(*range); range->addWatcher(this); // range->setWantsDirectChanges(true); } void DUContext::clearUseSmartRanges() { ENSURE_CAN_WRITE if (!m_dynamicData->m_rangesForUses.isEmpty()) { EditorIntegrator editor; editor.setCurrentUrl(url(), (bool)smartRange()); LockedSmartInterface iface = editor.smart(); if (iface) { foreach (SmartRange* range, m_dynamicData->m_rangesForUses) { range->removeWatcher(this); EditorIntegrator::releaseRange(range); } } m_dynamicData->m_rangesForUses.clear(); } } void DUContext::setUseDeclaration(int useNumber, int declarationIndex) { ENSURE_CAN_WRITE d_func_dynamic()->m_usesList()[useNumber].m_declarationIndex = declarationIndex; } DUContext * DUContext::findContextAt(const SimpleCursor & position) const { ENSURE_CAN_READ if (!range().contains(position)) return 0; FOREACH_FUNCTION(LocalIndexedDUContext child, d_func()->m_childContexts) if (DUContext* specific = child.data(topContext())->findContextAt(position)) return specific; return const_cast(this); } Declaration * DUContext::findDeclarationAt(const SimpleCursor & position) const { ENSURE_CAN_READ if (!range().contains(position)) return 0; FOREACH_FUNCTION(LocalIndexedDeclaration child, d_func()->m_localDeclarations) if (child.data(topContext())->range().contains(position)) return child.data(topContext()); return 0; } DUContext* DUContext::findContextIncluding(const SimpleRange& range) const { ENSURE_CAN_READ if (!this->range().contains(range)) return 0; FOREACH_FUNCTION(LocalIndexedDUContext child, d_func()->m_childContexts) if (DUContext* specific = child.data(topContext())->findContextIncluding(range)) return specific; return const_cast(this); } int DUContext::findUseAt(const SimpleCursor & position) const { ENSURE_CAN_READ synchronizeUsesFromSmart(); if (!range().contains(position)) return -1; for(unsigned int a = 0; a < d_func()->m_usesSize(); ++a) if (d_func()->m_uses()[a].m_range.contains(position)) return a; return -1; } bool DUContext::inSymbolTable() const { return d_func()->m_inSymbolTable; } void DUContext::setInSymbolTable(bool inSymbolTable) { if(parentContext()) { if(!d_func()->m_inSymbolTable && inSymbolTable) { QualifiedIdentifier id(scopeIdentifier(true)); PersistentSymbolTable::self().addContext(id, this); }else if(d_func()->m_inSymbolTable && !inSymbolTable) { QualifiedIdentifier id(scopeIdentifier(true)); PersistentSymbolTable::self().removeContext(id, this); } } d_func_dynamic()->m_inSymbolTable = inSymbolTable; } // kate: indent-width 2; void DUContext::clearImportedParentContexts() { ENSURE_CAN_WRITE DUCHAIN_D_DYNAMIC(DUContext); while( d->m_importedContextsSize() != 0 ) { if( d->m_importedContexts()[0].isBackwardMapped() ) { DUContext* ctx = d->m_importedContexts()[0].context(0); if(ctx) ctx->m_dynamicData->removeImportedChildContext(this); } - + d->m_importedContextsList().removeOne(d->m_importedContexts()[0]); } } void DUContext::cleanIfNotEncountered(const QSet& encountered) { ENSURE_CAN_WRITE foreach (Declaration* dec, localDeclarations()) if (!encountered.contains(dec)) delete dec; FOREACH_FUNCTION(LocalIndexedDUContext childContext, d_func()->m_childContexts) if (!encountered.contains(childContext.data(topContext()))) delete childContext.data(topContext()); } TopDUContext* DUContext::topContext() const { return m_dynamicData->m_topContext; } QWidget* DUContext::createNavigationWidget(Declaration* /*decl*/, TopDUContext* /*topContext*/, const QString& /*htmlPrefix*/, const QString& /*htmlSuffix*/) const { return 0; } void DUContext::squeeze() { if(!m_dynamicData->m_rangesForUses.isEmpty()) m_dynamicData->m_rangesForUses.squeeze(); FOREACH_FUNCTION(LocalIndexedDUContext child, d_func()->m_childContexts) child.data(topContext())->squeeze(); } QList allUses(DUContext* context, int declarationIndex, bool noEmptyUses) { QList ret; for(int a = 0; a < context->usesCount(); ++a) if(context->uses()[a].m_declarationIndex == declarationIndex) if(!noEmptyUses || !context->uses()[a].m_range.isEmpty()) ret << context->uses()[a].m_range; foreach(DUContext* child, context->childContexts()) ret += allUses(child, declarationIndex, noEmptyUses); return ret; } QList allSmartUses(DUContext* context, int declarationIndex) { QList ret; const Use* uses(context->uses()); for(int a = 0; a < context->usesCount(); ++a) if(uses[a].m_declarationIndex == declarationIndex) { KTextEditor::SmartRange* range = context->useSmartRange(a); if(range) ret << range; } foreach(DUContext* child, context->childContexts()) ret += allSmartUses(child, declarationIndex); return ret; } DUContext::SearchItem::SearchItem(const QualifiedIdentifier& id, Ptr nextItem, int start) : isExplicitlyGlobal(start == 0 ? id.explicitlyGlobal() : false) { if(!id.isEmpty()) { if(id.count() > start) identifier = id.at(start); if(id.count() > start+1) addNext(Ptr( new SearchItem(id, nextItem, start+1) )); else if(nextItem) next.append(nextItem); }else if(nextItem) { ///If there is no prefix, just copy nextItem isExplicitlyGlobal = nextItem->isExplicitlyGlobal; identifier = nextItem->identifier; next = nextItem->next; } } DUContext::SearchItem::SearchItem(const QualifiedIdentifier& id, const PtrList& nextItems, int start) : isExplicitlyGlobal(start == 0 ? id.explicitlyGlobal() : false) { if(id.count() > start) identifier = id.at(start); if(id.count() > start+1) addNext(Ptr( new SearchItem(id, nextItems, start+1) )); else next = nextItems; } DUContext::SearchItem::SearchItem(bool explicitlyGlobal, Identifier id, const PtrList& nextItems) : isExplicitlyGlobal(explicitlyGlobal), identifier(id), next(nextItems) { } DUContext::SearchItem::SearchItem(bool explicitlyGlobal, Identifier id, Ptr nextItem) : isExplicitlyGlobal(explicitlyGlobal), identifier(id) { next.append(nextItem); } bool DUContext::SearchItem::match(const QualifiedIdentifier& id, int offset) const { if(id.isEmpty()) { if(identifier.isEmpty() && next.isEmpty()) return true; else return false; } if(id.at(offset) != identifier) //The identifier is different return false; if(offset == id.count()-1) { if(next.isEmpty()) return true; //match else return false; //id is too short } for(int a = 0; a < next.size(); ++a) if(next[a]->match(id, offset+1)) return true; return false; } bool DUContext::SearchItem::isEmpty() const { return identifier.isEmpty(); } bool DUContext::SearchItem::hasNext() const { return !next.isEmpty(); } QList DUContext::SearchItem::toList(const QualifiedIdentifier& prefix) const { QList ret; QualifiedIdentifier id = prefix; if(id.isEmpty()) id.setExplicitlyGlobal(isExplicitlyGlobal); if(!identifier.isEmpty()) id.push(identifier); if(next.isEmpty()) { ret << id; } else { for(int a = 0; a < next.size(); ++a) ret += next[a]->toList(id); } return ret; } void DUContext::SearchItem::addNext(SearchItem::Ptr other) { next.append(other); } void DUContext::SearchItem::addToEachNode(SearchItem::Ptr other) { if(other->isExplicitlyGlobal) return; next.append(other); for(int a = 0; a < next.size()-1; ++a) next[a]->addToEachNode(other); } void DUContext::SearchItem::addToEachNode(SearchItem::PtrList other) { int added = 0; FOREACH_ARRAY(SearchItem::Ptr o, other) { if(!o->isExplicitlyGlobal) { next.append(o); ++added; } } for(int a = 0; a < next.size()-added; ++a) next[a]->addToEachNode(other); } DUContext::Import::Import(DUContext* _context, const SimpleCursor& _position) : position(_position) { if(_context && _context->owner() && _context->owner()->specialization()) { m_declaration = _context->owner()->id(true); }else{ m_context = _context; } } DUContext::Import::Import(const DeclarationId& id, const SimpleCursor& _position) : position(_position) { m_declaration = id; } DUContext* DUContext::Import::context(const TopDUContext* topContext) const { if(m_declaration.isValid()) { Declaration* decl = m_declaration.getDeclaration(topContext); if(decl) return decl->internalContext(); else return 0; }else{ return m_context.data(); } } bool DUContext::Import::isBackwardMapped() const { return true; //Always do backward-mapping. We just have to find a solution for temporary imports. /* if(m_declaration.isValid()) return !m_declaration.specialization(); else return true;*/ } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/identifier.h b/language/duchain/identifier.h index ecdbe8b22..b7d784ac8 100644 --- a/language/duchain/identifier.h +++ b/language/duchain/identifier.h @@ -1,368 +1,368 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda 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 IDENTIFIER_H #define IDENTIFIER_H #include #include #include #include #include #include #include "../languageexport.h" //We use shared d-pointers, which is even better than a d-pointer, but krazy probably won't get it, so exclude the test. //krazy:excludeall=dpointer namespace KDevelop { class TypeIdentifier; class Identifier; class QualifiedIdentifier; template class QualifiedIdentifierPrivate; template class IdentifierPrivate; class IndexedString; //A helper-class to store an identifier by index in a type-safe way. Will be extended to do reference-counting at some point. //The difference to Identifier is that this class only stores the index of an identifier that is in the repository, without any dynamic //abilities or access to the contained data. struct KDEVPLATFORMLANGUAGE_EXPORT IndexedIdentifier { IndexedIdentifier(); IndexedIdentifier(const Identifier& id); IndexedIdentifier& operator=(const Identifier& id); bool operator==(const IndexedIdentifier& rhs) const; bool operator!=(const IndexedIdentifier& rhs) const; bool operator==(const Identifier& id) const; bool isEmpty() const; Identifier identifier() const; operator Identifier() const; unsigned int index; }; //A helper-class to store an identifier by index in a type-safe way. Will be extended to do reference-counting at some point. //The difference to QualifiedIdentifier is that this class only stores the index of an identifier that is in the repository, without any dynamic //abilities or access to the contained data. struct KDEVPLATFORMLANGUAGE_EXPORT IndexedQualifiedIdentifier { IndexedQualifiedIdentifier(); IndexedQualifiedIdentifier(const QualifiedIdentifier& id); IndexedQualifiedIdentifier& operator=(const QualifiedIdentifier& id); bool operator==(const IndexedQualifiedIdentifier& rhs) const; bool operator==(const QualifiedIdentifier& id) const; bool operator<(const IndexedQualifiedIdentifier& rhs) const { return index < rhs.index; } - + bool isValid() const; QualifiedIdentifier identifier() const; operator QualifiedIdentifier() const; uint index; }; //A helper-class to store an identifier by index in a type-safe way. Will be extended to do reference-counting at some point. //The difference to QualifiedIdentifier is that this class only stores the index of an identifier that is in the repository, without any dynamic //abilities or access to the contained data. struct KDEVPLATFORMLANGUAGE_EXPORT IndexedTypeIdentifier { IndexedTypeIdentifier(); IndexedTypeIdentifier(const TypeIdentifier& id); IndexedTypeIdentifier& operator=(const TypeIdentifier& id); bool operator==(const IndexedTypeIdentifier& rhs) const; bool operator==(const TypeIdentifier& id) const; bool isValid() const; TypeIdentifier identifier() const; operator TypeIdentifier() const; uint index; }; /// Represents a single unqualified identifier class KDEVPLATFORMLANGUAGE_EXPORT Identifier { friend class QualifiedIdentifier; public: /** * @param start The position in the given string where to start searching for the identifier(optional). * @param takenRange If this is nonzero, it will be filled with the length of the range from the beginning of the given string, that was used to construct this identifier.(optional) * */ explicit Identifier(const QString& str, uint start = 0, uint* takenRange = 0); ///Preferred constructor, ise this if you already have an IndexedString available. This does not decompose the given string. explicit Identifier(const IndexedString& str); Identifier(const Identifier& rhs); Identifier(uint index); Identifier(); ~Identifier(); static Identifier unique(int token); bool isUnique() const; int uniqueToken() const; /// If \a token is non-zero, turns this Identifier into the special per-document /// Unique identifier, used for anonymous namespaces. /// Pass a token which is specific to the document to allow correct equality comparison. void setUnique(int token); const IndexedString identifier() const; void setIdentifier(const QString& identifier); //Should be preferred over the other version void setIdentifier(const IndexedString& identifier); // QString mangled() const; uint hash() const; /** * Comparison ignoring the template-identifiers * */ bool nameEquals(const Identifier& rhs) const; //Expensive TypeIdentifier templateIdentifier(int num) const; uint templateIdentifiersCount() const; void appendTemplateIdentifier(const TypeIdentifier& identifier); void clearTemplateIdentifiers(); void setTemplateIdentifiers(const QList& templateIdentifiers); QString toString() const; bool operator==(const Identifier& rhs) const; bool operator!=(const Identifier& rhs) const; Identifier& operator=(const Identifier& rhs); bool isEmpty() const; ///Returns a unique index within the global identifier repository for this identifier. ///If the identifier isn't in the repository yet, it is added to the repository. uint index() const; /** * kDebug(9505) stream operator. Writes this identifier to the debug output in a nicely formatted way. */ inline friend kdbgstream& operator<< (kdbgstream& s, const Identifier& identifier) { s << identifier.toString(); return s; } private: void makeConstant() const; void prepareWrite(); //Only one of the following pointers is valid at a given time mutable uint m_index; //Valid if cd is valid union { mutable IdentifierPrivate* dd; //Dynamic, owned by this identifier mutable const IdentifierPrivate* cd; //Constant, owned by the repository }; }; /** * Represents a qualified identifier * * QualifiedIdentifier has it's hash-values stored, so using the hash-values is very efficient. * */ class KDEVPLATFORMLANGUAGE_EXPORT QualifiedIdentifier { public: explicit QualifiedIdentifier(const QString& id); explicit QualifiedIdentifier(const Identifier& id); QualifiedIdentifier(const QualifiedIdentifier& id); QualifiedIdentifier(uint index); QualifiedIdentifier(); virtual ~QualifiedIdentifier(); void push(const Identifier& id); void push(const QualifiedIdentifier& id); //Pops one identifier from back: void pop(); void clear(); bool isEmpty() const; int count() const; Identifier first() const; Identifier last() const; Identifier top() const; const Identifier at(int i) const; /** * @param pos Position where to start the copy. * @param len If this is -1, the whole following part will be returned. * */ QualifiedIdentifier mid(int pos, int len = -1) const; /** * Copy the leftmost \a len number of identifiers. * - * @param len The number of identifiers to copy. + * @param len The number of identifiers to copy, or if negative, the number of identifiers to omit from the right * */ - inline QualifiedIdentifier left(int len) const { return mid(0, len); } + inline QualifiedIdentifier left(int len) const { return mid(0, len > 0 ? len : count() + len); } bool explicitlyGlobal() const; void setExplicitlyGlobal(bool eg); bool isQualified() const; ///A flag that can be set by setIsExpression bool isExpression() const; /** * Set the expression-flag, that can be retrieved by isExpression(). * This flag is not respected while creating the hash-value and while operator==() comparison. * It is respected while isSame(..) comparison. * */ void setIsExpression(bool); QString toString(bool ignoreExplicitlyGlobal = false) const; QStringList toStringList() const; // QString mangled() const; QualifiedIdentifier operator+(const QualifiedIdentifier& rhs) const; QualifiedIdentifier& operator+=(const QualifiedIdentifier& rhs); //Nicer interfaces to merge QualifiedIdentifier operator+(const Identifier& rhs) const; QualifiedIdentifier& operator+=(const Identifier& rhs); //Returns a QualifiedIdentifier with this one appended to the other. It is explicitly global if either this or base is. QualifiedIdentifier merge(const QualifiedIdentifier& base) const; // //The returned identifier will have explicitlyGlobal() set to false // QualifiedIdentifier strip(const QualifiedIdentifier& unwantedBase) const; /** * A more complex comparison than operator==(..). * It does respect the isExpression() flag, and optionally the explicitlyGlobal flag. * */ bool isSame(const QualifiedIdentifier& rhs, bool ignoreExplicitlyGlobal=true) const; /** * Computes the hash-value that would be created with a qualified identifier with hash-value @param leftHash of size @param leftSize * with @param appendIdentifier appended as an identifier * */ static uint combineHash(uint leftHash, uint leftSize, Identifier appendIdentifier); /**The comparison-operators do not respect explicitlyGlobal and isExpression, they only respect the real scope. * This is for convenient use in hash-tables etc. * */ bool operator==(const QualifiedIdentifier& rhs) const; bool operator!=(const QualifiedIdentifier& rhs) const; QualifiedIdentifier& operator=(const QualifiedIdentifier& rhs); #if 0 enum MatchTypes { NoMatch /**< matches no identifier */, EndsWith /**< The current identifier ends with the one given to the match function */, TargetEndsWith /**< The identifier given to the match function ends with the current identifier */, ExactMatch /**< matches if the identifiers match exactly */ }; MatchTypes match(const Identifier& other) const; MatchTypes match(const QualifiedIdentifier& other) const; #endif bool beginsWith(const QualifiedIdentifier& other) const; uint index() const; /** * kDebug(9505) stream operator. Writes this identifier to the debug output in a nicely formatted way. */ inline friend kdbgstream& operator<< (kdbgstream& s, const QualifiedIdentifier& identifier) { s << identifier.toString(); return s; } typedef uint HashType; ///The hash does not respect explicitlyGlobal, only the real scope. HashType hash() const; ///Finds all identifiers in the identifier-repository that have the given hash value static void findByHash(HashType hash, KDevVarLengthArray& target); protected: bool sameIdentifiers(const QualifiedIdentifier& rhs) const; void makeConstant() const; void prepareWrite(); mutable uint m_index; union { mutable QualifiedIdentifierPrivate* dd; mutable const QualifiedIdentifierPrivate* cd; }; }; /** * Extends QualifiedIdentifier by: * - Arbitrary count of pointer-poperators with cv-qualifiers * - Reference operator * All the properties set here are respected in the hash value. * */ class KDEVPLATFORMLANGUAGE_EXPORT TypeIdentifier : public QualifiedIdentifier { public: ///Variables like pointerDepth, isReference, etc. are not parsed from the string, so this parsing is quite limited. TypeIdentifier(); TypeIdentifier(const QString& str); TypeIdentifier(const QualifiedIdentifier& id); TypeIdentifier(const TypeIdentifier& id); TypeIdentifier(uint index); bool isReference() const; void setIsReference(bool); bool isConstant() const; void setIsConstant(bool); ///Returns the pointer depth. Example for C++: "char*" has pointer-depth 1, "char***" has pointer-depth 3 int pointerDepth() const; /**Sets the pointer-depth to the specified count * When the pointer-depth is increased, the "isConstPointer" values for new depths will be initialized with false. * For efficiency-reasons the maximum currently is 32. */ void setPointerDepth(int); ///Whether the target of pointer 'depthNumber' is constant bool isConstPointer(int depthNumber) const; void setIsConstPointer(int depthNumber, bool constant); bool isSame(const TypeIdentifier& rhs, bool ignoreExplicitlyGlobal=true) const; QString toString(bool ignoreExplicitlyGlobal = false) const; /**The comparison-operators do not respect explicitlyGlobal and isExpression, they only respect the real scope. * This is for convenient use in hash-tables etc. * */ bool operator==(const TypeIdentifier& rhs) const; bool operator!=(const TypeIdentifier& rhs) const; }; KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const TypeIdentifier& id); KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const QualifiedIdentifier& id); KDEVPLATFORMLANGUAGE_EXPORT uint qHash(const Identifier& id); } #endif // IDENTIFIER_H // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/repositories/itemrepository.cpp b/language/duchain/repositories/itemrepository.cpp index ee7d9874b..849584848 100644 --- a/language/duchain/repositories/itemrepository.cpp +++ b/language/duchain/repositories/itemrepository.cpp @@ -1,331 +1,331 @@ /* 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 "itemrepository.h" #include #include #include #include #include #include #include #include "../duchain.h" namespace KDevelop { uint staticItemRepositoryVersion() { //Increase this to reset incompatible item-repositories - return 49; + return 50; } AbstractItemRepository::~AbstractItemRepository() { } ItemRepositoryRegistry::ItemRepositoryRegistry(QString openPath, KLockFile::Ptr lock) : m_mutex(QMutex::Recursive) { if(!openPath.isEmpty()) open(openPath, false, lock); } QMutex& ItemRepositoryRegistry::mutex() { return m_mutex; } QAtomicInt& ItemRepositoryRegistry::getCustomCounter(const QString& identity, int initialValue) { if(!m_customCounters.contains(identity)) m_customCounters.insert(identity, new QAtomicInt(initialValue)); return *m_customCounters[identity]; } bool processExists(int pid) { ///@todo Find a cross-platform way of doing this! QFileInfo f(QString("/proc/%1").arg(pid)); return f.exists(); } QPair allocateRepository() { KLockFile::Ptr lock; QString repoPath; KComponentData component("item repositories temp", QByteArray(), KComponentData::SkipMainComponentRegistration); QString baseDir = QDir::homePath() + "/.kdevduchain"; KStandardDirs::makeDir(baseDir); //Since each instance of kdevelop needs an own directory, iterate until we find a not-yet-used one for(int a = 0; a < 100; ++a) { QString specificDir = baseDir + QString("/%1").arg(a); KStandardDirs::makeDir(specificDir); lock = new KLockFile(specificDir + "/lock", component); KLockFile::LockResult result = lock->lock(KLockFile::NoBlockFlag | KLockFile::ForceFlag); bool useDir = false; if(result != KLockFile::LockOK) { int pid; QString hostname, appname; if(lock->getLockInfo(pid, hostname, appname)) { if(!processExists(pid)) { kDebug() << "The process holding" << specificDir << "with pid" << pid << "does not exists any more. Re-using the directory."; QFile::remove(specificDir + "/lock"); useDir = true; if(lock->lock(KLockFile::NoBlockFlag | KLockFile::ForceFlag) != KLockFile::LockOK) { kWarning() << "Failed to re-establish the lock in" << specificDir; continue; } } } }else { useDir = true; } if(useDir) { repoPath = specificDir; if(result == KLockFile::LockStale) kWarning() << "stale lock detected:" << specificDir + "/lock"; break; } } if(repoPath.isEmpty()) { kError() << "could not create a directory for the duchain data"; }else{ kDebug() << "picked duchain directory" << repoPath; } return qMakePair(repoPath, lock); } ///The global item-repository registry that is used by default ItemRepositoryRegistry& allocateGlobalItemRepositoryRegistry() { QPair repo = allocateRepository(); static ItemRepositoryRegistry global(repo.first, repo.second); return global; } ///The global item-repository registry that is used by default ItemRepositoryRegistry& globalItemRepositoryRegistry() { static ItemRepositoryRegistry& global(allocateGlobalItemRepositoryRegistry()); return global; } void ItemRepositoryRegistry::registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager) { QMutexLocker lock(&m_mutex); m_repositories.insert(repository, manager); if(!m_path.isEmpty()) { if(!repository->open(m_path)) { deleteDataDirectory(); kError() << "failed to open a repository"; abort(); } } } QString ItemRepositoryRegistry::path() const { QMutexLocker lock(&m_mutex); return m_path; } void ItemRepositoryRegistry::lockForWriting() { QMutexLocker lock(&m_mutex); //Create is_writing QFile f(m_path + "/is_writing"); f.open(QIODevice::WriteOnly); f.close(); } void ItemRepositoryRegistry::unlockForWriting() { QMutexLocker lock(&m_mutex); //Delete is_writing QFile::remove(m_path + "/is_writing"); } void ItemRepositoryRegistry::unRegisterRepository(AbstractItemRepository* repository) { QMutexLocker lock(&m_mutex); Q_ASSERT(m_repositories.contains(repository)); repository->close(); m_repositories.remove(repository); } //Recursive delete, copied from a mailing-list //Returns true on success bool removeDirectory(const QDir &aDir) { bool has_err = false; if (aDir.exists())//QDir::NoDotAndDotDot { QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files); int count = entries.size(); for (int idx = 0; ((idx < count) && !has_err); idx++) { QFileInfo entryInfo = entries[idx]; QString path = entryInfo.absoluteFilePath(); if (entryInfo.isDir()) { has_err = !removeDirectory(QDir(path)); } else { QFile file(path); if (!file.remove()) has_err = true; } } if (!aDir.rmdir(aDir.absolutePath())) has_err = true; } return !has_err; } //After calling this, the data-directory may be a new one void ItemRepositoryRegistry::deleteDataDirectory() { QMutexLocker lock(&m_mutex); QFileInfo pathInfo(m_path); QDir d(m_path); //lockForWriting creates a file, that prevents any other KDevelop instance from using the directory as it is. //Instead, the other instance will try to delete the directory as well. lockForWriting(); // Have to release the lock here, else it will delete the new lock and windows needs all file-handles // to be released before deleting a directory that contains these files m_lock->unlock(); bool result = removeDirectory(d); Q_ASSERT(result); Q_ASSERT(m_lock); //Just remove the old directory, and allocate a new one. Probably it'll be the same one. QPair repo = allocateRepository(); m_path = repo.first; m_lock = repo.second; } bool ItemRepositoryRegistry::open(const QString& path, bool clear, KLockFile::Ptr lock) { QMutexLocker mlock(&m_mutex); if(m_path == path && !clear) return true; m_lock = lock; m_path = path; bool needRepoCheck = true; while(needRepoCheck) { if(QFile::exists(m_path + "/is_writing")) { kWarning() << "repository" << m_path << "was write-locked, it probably is inconsistent"; clear = true; } QDir pathDir(m_path); pathDir.setFilter(QDir::Files); //When there is only one file in the repository, it's the lock-file, and the repository has just been cleared if(pathDir.count() != 1 && !QFile::exists( m_path + QString("/version_%1").arg(staticItemRepositoryVersion()) )) { kWarning() << "version-hint not found, seems to be an old version"; clear = true; } if(clear) { kWarning() << QString("The data-repository at %1 has to be cleared.").arg(m_path); // KMessageBox::information( 0, i18n("The data-repository at %1 has to be cleared. Either the disk format has changed, or KDevelop crashed while writing the repository.", m_path ) ); deleteDataDirectory(); clear = false; //We need to re-check, because a new data-directory may have been picked }else{ needRepoCheck = false; } } foreach(AbstractItemRepository* repository, m_repositories.keys()) { if(!repository->open(path)) { deleteDataDirectory(); kError() << "failed to open a repository"; abort(); } } QFile f(path + "/Counters"); if(f.open(QIODevice::ReadOnly)) { QDataStream stream(&f); while(!stream.atEnd()) { //Read in all custom counter values QString counterName; stream >> counterName; int counterValue; stream >> counterValue; if(m_customCounters.contains(counterName)) *m_customCounters[counterName] = counterValue; else getCustomCounter(counterName, 0) = counterValue; } }else{ // kDebug() << "Could not open counter file"; } return true; } void ItemRepositoryRegistry::store() { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) repository->store(); QFile versionFile(m_path + QString("/version_%1").arg(staticItemRepositoryVersion())); if(versionFile.open(QIODevice::WriteOnly)) { versionFile.close(); }else{ kWarning() << "Could not open version file for writing"; } //Store all custom counter values QFile f(m_path + "/Counters"); if(f.open(QIODevice::WriteOnly)) { f.resize(0); QDataStream stream(&f); for(QMap::const_iterator it = m_customCounters.begin(); it != m_customCounters.end(); ++it) { stream << it.key(); stream << it.value()->fetchAndAddRelaxed(0); } }else{ kWarning() << "Could not open counter file for writing"; } } void ItemRepositoryRegistry::close() { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) repository->close(); m_path.clear(); } ItemRepositoryRegistry::~ItemRepositoryRegistry() { close(); QMutexLocker lock(&m_mutex); foreach(QAtomicInt* counter, m_customCounters) delete counter; } } diff --git a/language/duchain/topducontext.cpp b/language/duchain/topducontext.cpp index ef21dd6e6..940dd36f7 100644 --- a/language/duchain/topducontext.cpp +++ b/language/duchain/topducontext.cpp @@ -1,1577 +1,1576 @@ /* This is part of KDevelop 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 "topducontext.h" +#include "topducontextutils.h" #include #include #include "../editor/hashedstring.h" #include "../interfaces/iproblem.h" #include #include "persistentsymboltable.h" #include "declaration.h" #include "duchain.h" #include "duchainlock.h" #include "parsingenvironment.h" #include "duchainpointer.h" #include "declarationid.h" #include "namespacealiasdeclaration.h" #include "aliasdeclaration.h" #include "abstractfunctiondeclaration.h" #include "uses.h" #include "arrayhelpers.h" #include "topducontextdata.h" #include "duchainregister.h" #include "topducontextdynamicdata.h" // #define DEBUG_SEARCH using namespace KTextEditor; const uint maxApplyAliasesRecursion = 100; namespace std { #if defined(Q_CC_MSVC) using namespace stdext; #else using namespace __gnu_cxx; #endif } namespace KDevelop { Utils::BasicSetRepository recursiveImportRepository("Recursive Imports"); ReferencedTopDUContext::ReferencedTopDUContext(TopDUContext* context) : m_topContext(context) { if(m_topContext) DUChain::self()->refCountUp(m_topContext); } ReferencedTopDUContext::ReferencedTopDUContext(const ReferencedTopDUContext& rhs) : m_topContext(rhs.m_topContext) { if(m_topContext) DUChain::self()->refCountUp(m_topContext); } ReferencedTopDUContext::~ReferencedTopDUContext() { if(m_topContext) DUChain::self()->refCountDown(m_topContext); } ReferencedTopDUContext& ReferencedTopDUContext::operator=(const ReferencedTopDUContext& rhs) { if(m_topContext == rhs.m_topContext) return *this; - + if(m_topContext) DUChain::self()->refCountDown(m_topContext); - + m_topContext = rhs.m_topContext; - + if(m_topContext) DUChain::self()->refCountUp(m_topContext); return *this; } IndexedTopDUContext::IndexedTopDUContext(TopDUContext* context) { if(context) m_index = context->ownIndex(); else m_index = DummyMask; } bool IndexedTopDUContext::isLoaded() const { if(index()) return DUChain::self()->isInMemory(index()); else return false; } IndexedString IndexedTopDUContext::url() const { if(index()) return DUChain::self()->urlForIndex(index()); else return IndexedString(); } TopDUContext* IndexedTopDUContext::data() const { // ENSURE_CHAIN_READ_LOCKED if(index()) return DUChain::self()->chainForIndex(index()); else return 0; } DEFINE_LIST_MEMBER_HASH(TopDUContextData, m_usedDeclarationIds, DeclarationId) REGISTER_DUCHAIN_ITEM(TopDUContext); class TopDUContext::CacheData { public: CacheData(TopDUContextPointer _context) : context(_context) { } TopDUContextPointer context; }; struct TopDUContext::AliasChainElement { AliasChainElement() { // Creates invalid entries, but we need it fast because it's used to initialize all items in KDevVarLengthArray } AliasChainElement(const AliasChainElement* _prev, Identifier id) : previous(_prev), ownsPrevious(false), identifier(id), hash(0), length(0) { if(previous) { length = previous->length + 1; hash = QualifiedIdentifier::combineHash(previous->hash, previous->length, identifier); } else{ length = 1; hash = QualifiedIdentifier::combineHash(0, 0, identifier); } } //Computes the identifier represented by this chain element(generally the identifiers across the "previous" chain reversed //Returns an invalid identifier if the to be constructed identifier doesn't exist in the identifier repository QualifiedIdentifier qualifiedIdentifier() const { KDevVarLengthArray identifiers; ///@todo find faster ways of doing this //Use the known hash to find a matching QualifiedIdentifier from the repository, without //having to construct it. This should be efficient, since in most cases there should only be one //identifier with this hash QualifiedIdentifier::findByHash(hash, identifiers); for(int a = 0; a < identifiers.size(); ++a) { //Check whether there is an identifier that is equal const QualifiedIdentifier& current(identifiers[a]); if(current.explicitlyGlobal()) continue; //Skip explicitly global identifiers, since those will not be matched correctly const AliasChainElement* checkElement = this; bool mismatch = false; for(int scope = current.count()-1; scope >= 0; --scope) { if(!checkElement || current.at(scope) != checkElement->identifier) { mismatch = true; break; } checkElement = checkElement->previous; } if(!mismatch) return current; } - + //We do this so we don't create crap items in the qualified-identifier repository while searching. //When the qualified identifier doesn't exist, we don't need to search it. return QualifiedIdentifier(); } const AliasChainElement* previous; bool ownsPrevious; Identifier identifier; uint hash; uint length; }; template void removeFromVector(QVector& vec, const T& t) { for(int a =0; a < vec.size(); ++a) { if(vec[a] == t) { vec.remove(a); removeFromVector(vec, t); return; } } } QMutex importStructureMutex(QMutex::Recursive); template bool removeOneImport(Container& container, const DUContext* value) { for(int a = 0; a < container.size(); ++a) { if(container[a].context() == value) { removeFromArray(container, a); return true; } } return false; } //Contains data that is not shared among top-contexts that share their duchain entries class TopDUContextLocalPrivate { public: TopDUContextLocalPrivate (TopDUContext* ctxt, TopDUContext* sharedDataOwner, uint index) : m_ctxt(ctxt), m_sharedDataOwner(sharedDataOwner), m_ownIndex(index), m_inDuChain(false) { m_indexedRecursiveImports.insert(index); if(sharedDataOwner) { Q_ASSERT(0); //Not supported and to be removed Q_ASSERT(!sharedDataOwner->m_local->m_sharedDataOwner); sharedDataOwner->m_local->m_dataUsers.insert(m_ctxt); - + foreach(const DUContext::Import& import, m_sharedDataOwner->m_local->m_importedContexts) if(dynamic_cast(import.context(0))) dynamic_cast(import.context(0))->m_local->m_directImporters.insert(m_ctxt); } } - + mutable QHash m_threadCaches; - + TopDUContext::CacheData* currentCache() const { QHash::iterator it = m_threadCaches.find(QThread::currentThreadId()); if(it != m_threadCaches.end()) return *it; else return 0; } - + ~TopDUContextLocalPrivate() { //Either we use some other contexts data and have no users, or we own the data and have users that share it. QMutexLocker lock(&importStructureMutex); - + Q_ASSERT(!m_sharedDataOwner || m_dataUsers.isEmpty()); - + if(m_sharedDataOwner) { Q_ASSERT(m_sharedDataOwner->m_local->m_dataUsers.contains(m_ctxt)); m_sharedDataOwner->m_local->m_dataUsers.remove(m_ctxt); - + if(!m_sharedDataOwner->m_local->m_dataUsers.isEmpty()) { //this should not happen, because the users should always be deleted before the owner itself is deleted. Q_ASSERT(0); } - + foreach(const DUContext::Import& import, m_sharedDataOwner->m_local->m_importedContexts) if(DUChain::self()->isInMemory(import.topContextIndex()) && dynamic_cast(import.context(0))) dynamic_cast(import.context(0))->m_local->m_directImporters.remove(m_ctxt); } foreach(const DUContext::Import& import, m_importedContexts) if(DUChain::self()->isInMemory(import.topContextIndex()) && dynamic_cast(import.context(0))) dynamic_cast(import.context(0))->m_local->m_directImporters.remove(m_ctxt); } - + ///@todo Make all this work consistently together with import-caching - + //After loading, should rebuild the links void rebuildDynamicImportStructure() { //Currently we do not store the whole data in TopDUContextLocalPrivate, so we reconstruct it from what was stored by DUContext. Q_ASSERT(m_importedContexts.isEmpty()); - + FOREACH_FUNCTION(const DUContext::Import& import, m_ctxt->d_func()->m_importedContexts) { if(DUChain::self()->isInMemory(import.topContextIndex())) { TopDUContext* top = dynamic_cast(import.context(0)); Q_ASSERT(top); addImportedContextRecursively(top, false, true); } } FOREACH_FUNCTION(IndexedDUContext importer, m_ctxt->d_func()->m_importers) { if(DUChain::self()->isInMemory(importer.topContextIndex())) { TopDUContext* top = dynamic_cast(importer.context()); Q_ASSERT(top); top->m_local->addImportedContextRecursively(m_ctxt, false, true); } } } - + //Index of this top-context within the duchain //Since the data of top-contexts can be shared among multiple, this can be used to add imports that are local to this top-context. QVector m_importedContexts; // mutable bool m_haveImportStructure : 1; TopDUContext* m_ctxt; TopDUContext* m_sharedDataOwner; //Either the owner of the shared data, or zero if this context owns the data QSet m_dataUsers; //Set of all context that use the data of this context. - + QSet m_directImporters; - + ParsingEnvironmentFilePointer m_file; QList m_problems; uint m_ownIndex; - + bool m_inDuChain; - - + + void clearImportedContextsRecursively() { QMutexLocker lock(&importStructureMutex); - + // Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1); - + QSet > rebuild; foreach(const DUContext::Import &import, m_importedContexts) { TopDUContext* top = dynamic_cast(import.context(0)); if(top) { top->m_local->m_directImporters.remove(m_ctxt); - + foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextRecursively(top, false); - + removeImportedContextRecursion(top, top, 1, rebuild); QHash > b = top->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == top) removeImportedContextRecursion(top, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } } } - + m_importedContexts.clear(); rebuildImportStructureRecursion(rebuild); Q_ASSERT(m_recursiveImports.isEmpty()); // Q_ASSERT(m_recursiveImports.size() == m_indexedRecursiveImports.count()-1); } - + //Adds the context to this and all contexts that import this, and manages m_recursiveImports void addImportedContextRecursively(TopDUContext* context, bool temporary, bool local) { QMutexLocker lock(&importStructureMutex); context->m_local->m_directImporters.insert(m_ctxt); - + if(local) m_importedContexts << DUContext::Import(context); - + foreach(TopDUContext* user, m_dataUsers) user->m_local->addImportedContextRecursively(context, temporary, false); // context->m_local->needImportStructure(); addImportedContextRecursion(context, context, 1, temporary); - + if(!m_ctxt->usingImportsCache()) { QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) addImportedContextRecursion(context, it.key(), (*it).first+1, temporary); //Add contexts that were imported earlier into the given one } } //Removes the context from this and all contexts that import this, and manages m_recursiveImports void removeImportedContextRecursively(TopDUContext* context, bool local) { QMutexLocker lock(&importStructureMutex); context->m_local->m_directImporters.remove(m_ctxt); - + if(local) removeFromVector(m_importedContexts, DUContext::Import(context)); - + foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextRecursively(context, false); - + QSet > rebuild; removeImportedContextRecursion(context, context, 1, rebuild); if(!m_ctxt->usingImportsCache()) { QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context) removeImportedContextRecursion(context, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } } rebuildImportStructureRecursion(rebuild); } void removeImportedContextsRecursively(const QList& contexts, bool local) { QMutexLocker lock(&importStructureMutex); - + foreach(TopDUContext* user, m_dataUsers) user->m_local->removeImportedContextsRecursively(contexts, false); QSet > rebuild; foreach(TopDUContext* context, contexts) { - + context->m_local->m_directImporters.remove(m_ctxt); - + if(local) removeFromVector(m_importedContexts, DUContext::Import(context)); - + removeImportedContextRecursion(context, context, 1, rebuild); - + if(!m_ctxt->usingImportsCache()) { QHash > b = context->m_local->m_recursiveImports; for(RecursiveImports::const_iterator it = b.constBegin(); it != b.constEnd(); ++it) { if(m_recursiveImports.contains(it.key()) && m_recursiveImports[it.key()].second == context) removeImportedContextRecursion(context, it.key(), it->first+1, rebuild); //Remove all contexts that are imported through the context } } } rebuildImportStructureRecursion(rebuild); } /* void needImportStructure() const { return; //We always have an import-structure now*/ // if(m_haveImportStructure) // return; /* for(QVector::const_iterator parentIt = m_importedParentContexts.constBegin(); parentIt != m_importedParentContexts.constEnd(); ++parentIt) { TopDUContext* top = dynamic_cast(const_cast(parentIt->data())); //To avoid detaching, use const iterator if(top) { RecursiveImports::iterator it = m_recursiveImports.find(top); if(it == m_recursiveImports.end() || it->first != 1) { if(it == m_recursiveImports.end()) m_recursiveImports.insert(top, qMakePair(1, const_cast(top))); else *it = qMakePair(1, const_cast(top)); top->m_local->needImportStructure(); for(RecursiveImports::const_iterator importIt = top->m_local->m_recursiveImports.constBegin(); importIt != top->m_local->m_recursiveImports.constEnd(); ++importIt) { it = m_recursiveImports.find(importIt.key()); if(it == m_recursiveImports.end()) m_recursiveImports.insert(importIt.key(), qMakePair(importIt->first+1, const_cast(top))); else if(it->first > importIt->first+1) *it = qMakePair(importIt->first+1, const_cast(top)); //Found a shorter path } } } }*/ // m_haveImportStructure = true; // } //Has an entry for every single recursively imported file, that contains the shortest path, and the next context on that path to the imported context. //This does not need to be stored to disk, because it is defined implicitly. //What makes this most complicated is the fact that loops are allowed in the import structure. typedef QHash > RecursiveImports; mutable RecursiveImports m_recursiveImports; mutable TopDUContext::IndexedRecursiveImports m_indexedRecursiveImports; private: // void childClosure(QSet& children) { // if(children.contains(m_ctxt)) // return; // children.insert(m_ctxt); // for(QVector::const_iterator it = m_importedChildContexts.constBegin(); it != m_importedChildContexts.constEnd(); ++it) { // TopDUContext* top = dynamic_cast(const_cast(*it)); //We need to do const cast, to avoid senseless detaching // if(top) // top->m_local->childClosure(children); // } // } void addImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int depth, bool temporary = false) { if(m_ctxt->usingImportsCache()) return; - + // if(!m_haveImportStructure) // return; if(imported == m_ctxt) return; const bool computeShortestPaths = false; ///@todo We do not compute the shortest path. Think what's right. // traceNext->m_local->needImportStructure(); // imported->m_local->needImportStructure(); RecursiveImports::iterator it = m_recursiveImports.find(imported); if(it == m_recursiveImports.end()) { //Insert new path to "imported" m_recursiveImports[imported] = qMakePair(depth, traceNext); m_indexedRecursiveImports.insert(imported->indexed()); // Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size()+1); - + Q_ASSERT(traceNext != m_ctxt); }else{ if(!computeShortestPaths) return; if(temporary) //For temporary imports, we don't record the best path. return; //It would be better if we would use the following code, but it creates too much cost in updateImportedContextRecursion when imports are removed again. //Check whether the new way to "imported" is shorter than the stored one if((*it).first > depth) { //Add a shorter path (*it).first = depth; Q_ASSERT(traceNext); (*it).second = traceNext; Q_ASSERT(traceNext == imported || (traceNext->m_local->m_recursiveImports.contains(imported) && traceNext->m_local->m_recursiveImports[imported].first < (*it).first)); }else{ //The imported context is already imported through a same/better path, so we can just stop processing. This saves us from endless recursion. return; } } if(temporary) return; for(QSet::const_iterator it = m_directImporters.constBegin(); it != m_directImporters.constEnd(); ++it) { TopDUContext* top = dynamic_cast(const_cast(*it)); //Avoid detaching, so use const_cast if(top) ///@todo also record this for local imports top->m_local->addImportedContextRecursion(m_ctxt, imported, depth+1); } } void removeImportedContextRecursion(const TopDUContext* traceNext, const TopDUContext* imported, int distance, QSet >& rebuild) { if(m_ctxt->usingImportsCache()) return; - + if(imported == m_ctxt) return; // if(!m_haveImportStructure) // return; RecursiveImports::iterator it = m_recursiveImports.find(imported); if(it == m_recursiveImports.end()) { //We don't import. Just return, this saves us from endless recursion. return; }else{ //Check whether we have imported "imported" through "traceNext". If not, return. Else find a new trace. if((*it).second == traceNext && (*it).first == distance) { //We need to remove the import through traceNext. Check whether there is another imported context that imports it. m_recursiveImports.erase(it); //In order to prevent problems, we completely remove everything, and re-add it. //Just updating these complex structures is very hard. Q_ASSERT(imported != m_ctxt); m_indexedRecursiveImports.remove(imported->indexed()); // Q_ASSERT(m_indexedRecursiveImports.size() == m_recursiveImports.size()); rebuild.insert(qMakePair(m_ctxt, imported)); //We MUST do this before finding another trace, because else we would create loops for(QSet::const_iterator childIt = m_directImporters.constBegin(); childIt != m_directImporters.constEnd(); ++childIt) { TopDUContext* top = dynamic_cast(const_cast(*childIt)); //Avoid detaching, so use const iterator if(top) top->m_local->removeImportedContextRecursion(m_ctxt, imported, distance+1, rebuild); //Don't use 'it' from here on, it may be invalid } } } } //Updates the trace to 'imported' void rebuildStructure(const TopDUContext* imported); void rebuildImportStructureRecursion(const QSet >& rebuild) { for(QSet >::const_iterator it = rebuild.constBegin(); it != rebuild.constEnd(); ++it) { //for(int a = rebuild.size()-1; a >= 0; --a) { //Find the best imported parent it->first->m_local->rebuildStructure(it->second); } } }; struct TopDUContext::ContextChecker { ContextChecker(const TopDUContext* _top, const SimpleCursor& _position, ContextType _contextType, DUContext::SearchFlags _flags) : top(_top), position(_position), contextType(_contextType), flags(_flags) { } bool operator()(IndexedDUContext context) const { - + // const TopDUContext* otherTop = context->topContext(); if (top->m_local->m_ownIndex != context.topContextIndex()) { // Make sure that this declaration is accessible DUContext* ctx = context.data(); if(!ctx) return false; - + if (ctx->type() != contextType) return false; } else { DUContext* ctx = context.data(); if(!ctx) return false; - + if (ctx->type() != contextType) return false; if (ctx->range().start > position) if(!ctx->parentContext() || ctx->parentContext()->type() != Class) return false; } //Success return true; } const TopDUContext* top; const SimpleCursor& position; ContextType contextType; DUContext::SearchFlags flags; }; ///Takes a set of conditions in the constructors, and checks with each call to operator() whether these conditions are fulfilled on the given declaration. ///The import-structure needs to be constructed and locked when this is used -struct TopDUContext::DeclarationChecker { - DeclarationChecker(const TopDUContext* _top, const SimpleCursor& _position, const AbstractType::Ptr& _dataType, DUContext::SearchFlags _flags, KDevVarLengthArray* _createVisibleCache = 0) : createVisibleCache(_createVisibleCache), top(_top), topDFunc(_top->d_func()), position(_position), dataType(_dataType), flags(_flags) { - } - - bool operator()(IndexedDeclaration dec) const { +TopDUContext::DeclarationChecker::DeclarationChecker(const TopDUContext* _top, const SimpleCursor& _position, const AbstractType::Ptr& _dataType, DUContext::SearchFlags _flags, KDevVarLengthArray* _createVisibleCache) + : createVisibleCache(_createVisibleCache) + , top(_top) + , topDFunc(_top->d_func()) + , position(_position) + , dataType(_dataType) + , flags(_flags) +{ +} - if (top->m_local->m_ownIndex != dec.topContextIndex()) { +bool TopDUContext::DeclarationChecker::operator()(IndexedDeclaration dec) const +{ + if (top->m_local->m_ownIndex != dec.topContextIndex()) { // bool visible; // { // QMutexLocker lock(&importStructureMutex); // visible = top->m_local->m_indexedRecursiveImports.contains(dec.topContextIndex()); // } // if(createVisibleCache && visible) // createVisibleCache->append(dec); - // Make sure that this declaration is accessible + // Make sure that this declaration is accessible /* if (!(flags & DUContext::NoImportsCheck) && !visible) - return false;*/ - - Declaration* decl = dec.data(); - if(!decl) - return false; - - if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) - return false; + return false;*/ - if (dataType && decl->abstractType()->indexed() != dataType->indexed()) - // The declaration doesn't match the type filter we are applying - return false; + Declaration* decl = dec.data(); + if(!decl) + return false; - } else { + if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) + return false; + + if (dataType && decl->abstractType()->indexed() != dataType->indexed()) + // The declaration doesn't match the type filter we are applying + return false; + + } else { // if(createVisibleCache) // createVisibleCache->append(dec); - Declaration* decl = dec.data(); - if(!decl) - return false; - - if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) - return false; - - if (dataType && decl->abstractType() != dataType) - // The declaration doesn't match the type filter we are applying - return false; + Declaration* decl = dec.data(); + if(!decl) + return false; - if (decl->range().start >= position) - if(!decl->context() || decl->context()->type() != DUContext::Class) - return false; // The declaration is behind the position we're searching from, therefore not accessible - } - // Success, this declaration is accessible - return true; - } + if((flags & DUContext::OnlyFunctions) && !dynamic_cast(decl)) + return false; - mutable KDevVarLengthArray* createVisibleCache; - const TopDUContext* top; - const TopDUContextData* topDFunc; - const SimpleCursor& position; - const AbstractType::Ptr& dataType; - DUContext::SearchFlags flags; -}; + if (dataType && decl->abstractType() != dataType) + // The declaration doesn't match the type filter we are applying + return false; + + if (decl->range().start >= position) + if(!decl->context() || decl->context()->type() != DUContext::Class) + return false; // The declaration is behind the position we're searching from, therefore not accessible + } + // Success, this declaration is accessible + return true; +} // ImportTrace TopDUContext::importTrace(const TopDUContext* target) const // { // ImportTrace ret; // importTrace(target, ret); // return ret; // } -// +// // void TopDUContext::importTrace(const TopDUContext* target, ImportTrace& store) const // { // QMutexLocker lock(&importStructureMutex); -// +// // const TopDUContext* current = this; // while(current != target) { // // current->d_func()->needImportStructure(); -// +// // TopDUContextLocalPrivate::RecursiveImports::const_iterator it = current->m_local->m_recursiveImports.constFind(target); -// +// // if(it == current->m_local->m_recursiveImports.constEnd()) // return; -// +// // const TopDUContext* nextContext = (*it).second; -// +// // if(nextContext) { // store.append(ImportTraceItem(current, current->importPosition(nextContext))); -// +// // current = nextContext; // }else{ // kWarning() << "inconsistent import-structure"; // return; // } // } // } const TopDUContext::IndexedRecursiveImports& TopDUContext::recursiveImportIndices() const { // No lock-check for performance reasons QMutexLocker lock(&importStructureMutex); if(!d_func()->m_importsCache.isEmpty()) return d_func()->m_importsCache; - + return m_local->m_indexedRecursiveImports; } void TopDUContextData::updateImportCacheRecursion(uint baseIndex, IndexedTopDUContext currentContext, TopDUContext::IndexedRecursiveImports& visited) { if(visited.contains(currentContext.index())) return; Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called if(!currentContext.data()) { kDebug() << "importing invalid context"; return; } visited.insert(currentContext.index()); - + const TopDUContextData* currentData = currentContext.data()->topContext()->d_func(); if(currentData->m_importsCache.contains(baseIndex) || currentData->m_importsCache.isEmpty()) { //If we have a loop or no imports-cache is used, we have to look at each import separately. const KDevelop::DUContext::Import* imports = currentData->m_importedContexts(); uint importsSize = currentData->m_importedContextsSize(); for(uint a = 0; a < importsSize; ++a) { IndexedTopDUContext next(imports[a].topContextIndex()); if(next.isValid()) updateImportCacheRecursion(baseIndex, next, visited); } }else{ //If we don't have a loop with baseIndex, we can safely just merge with the imported importscache visited += currentData->m_importsCache; } } void TopDUContextData::updateImportCacheRecursion(IndexedTopDUContext currentContext, std::set& visited) { if(visited.find(currentContext.index()) != visited.end()) return; Q_ASSERT(currentContext.index()); //The top-context must be in the repository when this is called if(!currentContext.data()) { kDebug() << "importing invalid context"; return; } visited.insert(currentContext.index()); const TopDUContextData* currentData = currentContext.data()->topContext()->d_func(); const KDevelop::DUContext::Import* imports = currentData->m_importedContexts(); uint importsSize = currentData->m_importedContextsSize(); for(uint a = 0; a < importsSize; ++a) { IndexedTopDUContext next(imports[a].topContextIndex()); if(next.isValid()) updateImportCacheRecursion(next, visited); } } void TopDUContext::updateImportsCache() { QMutexLocker lock(&importStructureMutex); - - + + const bool use_fully_recursive_import_cache_computation = false; - + if(use_fully_recursive_import_cache_computation) { std::set visited; TopDUContextData::updateImportCacheRecursion(this, visited); Q_ASSERT(visited.find(ownIndex()) != visited.end()); d_func_dynamic()->m_importsCache = IndexedRecursiveImports(visited); }else{ d_func_dynamic()->m_importsCache = IndexedRecursiveImports(); TopDUContextData::updateImportCacheRecursion(ownIndex(), this, d_func_dynamic()->m_importsCache); } Q_ASSERT(d_func_dynamic()->m_importsCache.contains(IndexedTopDUContext(this))); Q_ASSERT(usingImportsCache()); Q_ASSERT(imports(this, SimpleCursor::invalid())); } bool TopDUContext::usingImportsCache() const { return !d_func()->m_importsCache.isEmpty(); } SimpleCursor TopDUContext::importPosition(const DUContext* target) const { ENSURE_CAN_READ DUCHAIN_D(DUContext); Import import(const_cast(target), SimpleCursor::invalid()); for(unsigned int a = 0; a < d->m_importedContextsSize(); ++a) if(d->m_importedContexts()[a] == import) return d->m_importedContexts()[a].position; - + return DUContext::importPosition(target); } void TopDUContextLocalPrivate::rebuildStructure(const TopDUContext* imported) { if(m_ctxt == imported) return; for(QVector::const_iterator parentIt = m_importedContexts.constBegin(); parentIt != m_importedContexts.constEnd(); ++parentIt) { TopDUContext* top = dynamic_cast(const_cast(parentIt->context(0))); //To avoid detaching, use const iterator if(top) { // top->m_local->needImportStructure(); if(top == imported) { addImportedContextRecursion(top, imported, 1); }else{ RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.find(imported); if(it2 != top->m_local->m_recursiveImports.end()) { addImportedContextRecursion(top, imported, (*it2).first + 1); } } } } - + for(unsigned int a = 0; a < m_ctxt->d_func()->m_importedContextsSize(); ++a) { TopDUContext* top = dynamic_cast(const_cast(m_ctxt->d_func()->m_importedContexts()[a].context(0))); //To avoid detaching, use const iterator if(top) { // top->m_local->needImportStructure(); if(top == imported) { addImportedContextRecursion(top, imported, 1); }else{ RecursiveImports::const_iterator it2 = top->m_local->m_recursiveImports.find(imported); if(it2 != top->m_local->m_recursiveImports.end()) { addImportedContextRecursion(top, imported, (*it2).first + 1); } } } } } void TopDUContext::rebuildDynamicImportStructure() { m_local->rebuildDynamicImportStructure(); } void TopDUContext::rebuildDynamicData(DUContext* parent, uint ownIndex) { Q_ASSERT(parent == 0 && ownIndex != 0); m_local->m_ownIndex = ownIndex; DUContext::rebuildDynamicData(parent, 0); } IndexedTopDUContext TopDUContext::indexed() const { return IndexedTopDUContext(m_local->m_ownIndex); } uint TopDUContext::ownIndex() const { return m_local->m_ownIndex; } TopDUContext::TopDUContext(TopDUContextData& data) : DUContext(data), m_local(new TopDUContextLocalPrivate(this, 0, data.m_ownIndex)), m_dynamicData(new TopDUContextDynamicData(this)) { } TopDUContext::TopDUContext(const IndexedString& url, const SimpleRange& range, ParsingEnvironmentFile* file) : DUContext(*new TopDUContextData(url), range), m_local(new TopDUContextLocalPrivate(this, 0, DUChain::newTopContextIndex())), m_dynamicData(new TopDUContextDynamicData(this)) { d_func_dynamic()->setClassId(this); - + DUCHAIN_D_DYNAMIC(TopDUContext); d->m_features = VisibleDeclarationsAndContexts; d->m_ownIndex = m_local->m_ownIndex; m_local->m_file = ParsingEnvironmentFilePointer(file); setInSymbolTable(true); } TopDUContext::TopDUContext(TopDUContext* sharedDataOwner, ParsingEnvironmentFile* file) : DUContext(*sharedDataOwner), m_local(new TopDUContextLocalPrivate(this, sharedDataOwner, DUChain::newTopContextIndex())), m_dynamicData(sharedDataOwner->m_dynamicData) { m_local->m_file = ParsingEnvironmentFilePointer(file); ///@todo this is incompatible with data-sharing, remove that option again. d_func_dynamic()->m_ownIndex = m_local->m_ownIndex; } KSharedPtr TopDUContext::parsingEnvironmentFile() const { return m_local->m_file; } TopDUContext::~TopDUContext( ) { if(!m_local->m_sharedDataOwner) { m_dynamicData->m_deleting = true; if(!isOnDisk()) clearUsedDeclarationIndices(); } deleteChildContextsRecursively(); deleteLocalDeclarations(); m_dynamicData->clearContextsAndDeclartions(); } void TopDUContext::deleteSelf() { //We've got to make sure that m_dynamicData and m_local are still valid while all the sub-contexts are destroyed TopDUContextLocalPrivate* local = m_local; TopDUContextDynamicData* dynamicData = m_dynamicData; - + if(!m_local->m_sharedDataOwner) m_dynamicData->m_deleting = true; - + delete this; - + delete local; delete dynamicData; } TopDUContext::Features TopDUContext::features() const { return d_func()->m_features; } void TopDUContext::setFeatures(Features features) { features = (TopDUContext::Features)(features & (~((uint)8))); //Remove the "Recursive" flag since that's only for searching features = (TopDUContext::Features)(features & (~ForceUpdateRecursive)); //Remove the update flags d_func_dynamic()->m_features = features; - + //Replicate features to ParsingEnvironmentFile if(parsingEnvironmentFile()) parsingEnvironmentFile()->setFeatures(features); } void TopDUContext::setParsingEnvironmentFile(ParsingEnvironmentFile* file) { m_local->m_file = KSharedPtr(file); - + //Replicate features to ParsingEnvironmentFile if(file) file->setFeatures(d_func()->m_features); } ///Decides whether the cache contains a valid list of visible declarations for the given hash. ///@param hash The hash-value, @param data The cache @param items Will be filled with the cached declarations. Will be left alone if none were found. /*void eventuallyUseCache(uint hash, TopDUContext::CacheData* cache, const IndexedDeclaration*& items, uint& itemCount) { //Check whether we have all visible global items cached TopDUContext::CacheData::HashType::iterator it = cache->visibleDeclarations.find( hash ); if( it != cache->visibleDeclarations.end() ) { itemCount = (uint)(*it).second.size(); items = (*it).second.constData(); } }*/ struct TopDUContext::FindDeclarationsAcceptor { FindDeclarationsAcceptor(const TopDUContext* _top, DeclarationList& _target, const DeclarationChecker& _check) : top(_top), target(_target), check(_check) { cache = _top->m_local->currentCache(); } void operator() (const AliasChainElement& element) { #ifdef DEBUG_SEARCH kDebug() << "accepting" << element.qualifiedIdentifier().toString(); #endif PersistentSymbolTable::Declarations allDecls; - + QualifiedIdentifier id = element.qualifiedIdentifier(); - + //If the identifier wasn't found in the identifier repository, the item cannot exist. if(id.isEmpty() && !element.identifier.isEmpty()) return; - + //This iterator efficiently filters the visible declarations out of all declarations PersistentSymbolTable::FilteredDeclarationIterator filter; - + //This is used if filterung is disabled PersistentSymbolTable::Declarations::Iterator unchecked; if(check.flags & DUContext::NoImportsCheck) { allDecls = PersistentSymbolTable::self().getDeclarations(id); unchecked = allDecls.iterator(); } else filter = PersistentSymbolTable::self().getFilteredDeclarations(id, top->recursiveImportIndices()); - + while(filter || unchecked) { - + IndexedDeclaration iDecl; if(filter) { iDecl = *filter; ++filter; } else { iDecl = *unchecked; ++unchecked; } - + if(!check(iDecl)) continue; Declaration* decl = iDecl.data(); if(!decl) continue; if( decl->kind() == Declaration::Alias ) { //Apply alias declarations AliasDeclaration* alias = static_cast(decl); if(alias->aliasedDeclaration().isValid()) { decl = alias->aliasedDeclaration().declaration(); } else { kDebug() << "lost aliased declaration"; } } target.append(decl); } check.createVisibleCache = 0; } const TopDUContext* top; CacheData* cache; DeclarationList& target; const DeclarationChecker& check; }; bool TopDUContext::findDeclarationsInternal(const SearchItem::PtrList& identifiers, const SimpleCursor& position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* /*source*/, SearchFlags flags) const { ENSURE_CAN_READ #ifdef DEBUG_SEARCH FOREACH_ARRAY(SearchItem::Ptr idTree, identifiers) foreach(const QualifiedIdentifier &id, idTree->toList()) kDebug() << "findDeclarationsInternal" << id.toString(); #endif DeclarationChecker check(this, position, dataType, flags); FindDeclarationsAcceptor storer(this, ret, check); ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindDeclarationsAcceptor. ///That stores the found declaration to the output. applyAliases(identifiers, storer, position, false); return true; } //This is used to prevent endless recursion due to "using namespace .." declarations, by storing all imports that are already being used. struct TopDUContext::ApplyAliasesBuddyInfo { ApplyAliasesBuddyInfo(uint importChainType, ApplyAliasesBuddyInfo* predecessor, IndexedQualifiedIdentifier importId) : m_importChainType(importChainType), m_predecessor(predecessor), m_importId(importId) { if(m_predecessor && m_predecessor->m_importChainType != importChainType) m_predecessor = 0; } //May also be called when this is zero. bool alreadyImporting(IndexedQualifiedIdentifier id) { ApplyAliasesBuddyInfo* current = this; while(current) { if(current->m_importId == id) return true; current = current->m_predecessor; } return false; } - + uint m_importChainType; ApplyAliasesBuddyInfo* m_predecessor; IndexedQualifiedIdentifier m_importId; }; ///@todo Implement a cache so at least the global import checks don't need to be done repeatedly. The cache should be thread-local, using DUChainPointer for the hashed items, and when an item was deleted, it should be discarded template void TopDUContext::applyAliases( const AliasChainElement* backPointer, const SearchItem::Ptr& identifier, Acceptor& accept, const SimpleCursor& position, bool canBeNamespace, ApplyAliasesBuddyInfo* buddy, uint recursionDepth ) const { if(recursionDepth > maxApplyAliasesRecursion) { QList searches = identifier->toList(); QualifiedIdentifier id; if(!searches.isEmpty()) id = searches.first(); - + kDebug() << "maximum apply-aliases recursion reached while searching" << id; } ///@todo explicitlyGlobal if the first identifier los global bool foundAlias = false; AliasChainElement newElement(backPointer, identifier->identifier); //Back-pointer for following elements. Also contains current hash and length. #ifdef DEBUG_SEARCH kDebug() << "checking" << newElement.qualifiedIdentifier().toString(); #endif if( !identifier->next.isEmpty() || canBeNamespace ) { //If it cannot be a namespace, the last part of the scope will be ignored QualifiedIdentifier id = newElement.qualifiedIdentifier(); if(!id.isEmpty()) { //This iterator efficiently filters the visible declarations out of all declarations PersistentSymbolTable::FilteredDeclarationIterator filter = PersistentSymbolTable::self().getFilteredDeclarations(id, recursiveImportIndices()); if(filter) { DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, 0); //The first part of the identifier has been found as a namespace-alias. //In c++, we only need the first alias. However, just to be correct, follow them all for now. for(; filter; ++filter) { IndexedDeclaration iDecl(*filter); - + if(!check(iDecl)) continue; Declaration* aliasDecl = iDecl.data(); if(!aliasDecl) continue; if(aliasDecl->kind() != Declaration::NamespaceAlias) continue; if(foundAlias) break; if(aliasDecl->identifier() != newElement.identifier) //Since we have retrieved the aliases by hash only, we still need to compare the name continue; Q_ASSERT(dynamic_cast(aliasDecl)); NamespaceAliasDeclaration* alias = static_cast(aliasDecl); foundAlias = true; QualifiedIdentifier importIdentifier = alias->importIdentifier(); if(importIdentifier.isEmpty()) { kDebug() << "found empty import"; continue; } - + if(buddy->alreadyImporting( importIdentifier )) continue; //This import has already been applied to this search //Create a chain of AliasChainElements that represent the identifier uint count = importIdentifier.count(); KDevVarLengthArray newChain; newChain.resize(count); for(uint a = 0; a < count; ++a) newChain[a] = AliasChainElement(a == 0 ? 0 : &newChain[a-1], importIdentifier.at(a)); AliasChainElement* newAliasedElement = &newChain[importIdentifier.count()-1]; ApplyAliasesBuddyInfo info(1, buddy, importIdentifier); - + if(identifier->next.isEmpty()) { //Just insert the aliased namespace identifier accept(*newAliasedElement); }else{ //Create an identifiers where namespace-alias part is replaced with the alias target FOREACH_ARRAY(SearchItem::Ptr item, identifier->next) applyAliases(newAliasedElement, item, accept, position, canBeNamespace, &info, recursionDepth+1); } } } }else{ if(!identifier->identifier.isEmpty()) return; //Normally the id should not be empty, but it is, so it wasn't found in the repo. Nothing to do. } } if(!foundAlias) { //If we haven't found an alias, put the current versions into the result list. Additionally we will compute the identifiers transformed through "using". if(identifier->next.isEmpty()) { accept(newElement); //We're at the end of a qualified identifier, accept it } else { FOREACH_ARRAY(SearchItem::Ptr next, identifier->next) applyAliases(&newElement, next, accept, position, canBeNamespace, 0, recursionDepth+1); } } /*if( !prefix.explicitlyGlobal() || !prefix.isEmpty() ) {*/ ///@todo check iso c++ if using-directives should be respected on top-level when explicitly global ///@todo this is bad for a very big repository(the chains should be walked for the top-context instead) //Find all namespace-imports at given scope #ifdef DEBUG_SEARCH kDebug() << "checking imports in" << (backPointer ? backPointer->qualifiedIdentifier().toString() : QString("global")); #endif { AliasChainElement importChainItem(backPointer, globalImportIdentifier); QualifiedIdentifier id = importChainItem.qualifiedIdentifier(); - + if(!id.isEmpty()) { //This iterator efficiently filters the visible declarations out of all declarations PersistentSymbolTable::FilteredDeclarationIterator filter = PersistentSymbolTable::self().getFilteredDeclarations(id, recursiveImportIndices()); - - + + if(filter) { DeclarationChecker check(this, position, AbstractType::Ptr(), NoSearchFlags, 0); for(; filter; ++filter) { //We must never break or return from this loop, because else we might be creating a bad cache if(!check(*filter)) continue; Declaration* importDecl = filter->data(); if(!importDecl) continue; //Search for the identifier with the import-identifier prepended Q_ASSERT(dynamic_cast(importDecl)); NamespaceAliasDeclaration* alias = static_cast(importDecl); #ifdef DEBUG_SEARCH kDebug() << "found import of" << alias->importIdentifier().toString(); #endif QualifiedIdentifier importIdentifier = alias->importIdentifier(); if(importIdentifier.isEmpty()) { kDebug() << "found empty import"; continue; } if(buddy->alreadyImporting( importIdentifier )) continue; //This import has already been applied to this search ApplyAliasesBuddyInfo info(2, buddy, importIdentifier); int count = importIdentifier.count(); KDevVarLengthArray newChain; newChain.resize(importIdentifier.count()); for(int a = 0; a < count; ++a) newChain[a] = AliasChainElement(a == 0 ? 0 : &newChain[a-1], importIdentifier.at(a)); AliasChainElement* newAliasedElement = &newChain[count-1]; #ifdef DEBUG_SEARCH kDebug() << "imported" << newAliasedElement->qualifiedIdentifier().toString(); #endif //Prevent endless recursion by checking whether we're actually doing a change if(!backPointer || newAliasedElement->hash != backPointer->hash || newAliasedElement->qualifiedIdentifier() != backPointer->qualifiedIdentifier()) applyAliases(newAliasedElement, identifier, accept, importDecl->topContext() == this ? importDecl->range().start : position, canBeNamespace, &info, recursionDepth+1); } } } } } template void TopDUContext::applyAliases( const SearchItem::PtrList& identifiers, Acceptor& acceptor, const SimpleCursor& position, bool canBeNamespace ) const { FOREACH_ARRAY(SearchItem::Ptr item, identifiers) applyAliases(0, item, acceptor, position, canBeNamespace, 0, 0); } struct TopDUContext::FindContextsAcceptor { FindContextsAcceptor(const TopDUContext* _top, QList& _target, const ContextChecker& _check) : top(_top), target(_target), check(_check) { cache = _top->m_local->currentCache(); } void operator() (const AliasChainElement& element) { #ifdef DEBUG_SEARCH kDebug() << "accepting" << element.qualifiedIdentifier().toString(); #endif PersistentSymbolTable::Contexts allDecls; - + QualifiedIdentifier id = element.qualifiedIdentifier(); - + //If the identifier wasn't found in the identifier repository, the item cannot exist. if(id.isEmpty() && !element.identifier.isEmpty()) return; - + //This iterator efficiently filters the visible declarations out of all declarations PersistentSymbolTable::FilteredDUContextIterator filter; - + //This is used if filterung is disabled PersistentSymbolTable::Contexts::Iterator unchecked; if(check.flags & DUContext::NoImportsCheck) { allDecls = PersistentSymbolTable::self().getContexts(id); unchecked = allDecls.iterator(); } else filter = PersistentSymbolTable::self().getFilteredContexts(id, top->recursiveImportIndices()); while(filter || unchecked) { - + IndexedDUContext iDecl; if(filter) { iDecl = *filter; ++filter; } else { iDecl = *unchecked; ++unchecked; } if(!check(iDecl)) continue; DUContext* ctx = iDecl.data(); if(!ctx) continue; target << ctx; } } const TopDUContext* top; CacheData* cache; QList& target; const ContextChecker& check; }; void TopDUContext::findContextsInternal(ContextType contextType, const SearchItem::PtrList& baseIdentifiers, const SimpleCursor& position, QList& ret, const TopDUContext* source, SearchFlags flags) const { ENSURE_CAN_READ ContextChecker check(this, position, contextType, flags & DUContext::NoImportsCheck); FindContextsAcceptor storer(this, ret, check); ///The actual scopes are found within applyAliases, and each complete qualified identifier is given to FindContextsAcceptor. ///That stores the found declaration to the output. applyAliases(baseIdentifiers, storer, position, contextType == Namespace); } TopDUContext* TopDUContext::sharedDataOwner() const { return m_local->m_sharedDataOwner; } TopDUContext * TopDUContext::topContext() const { return const_cast(this); } bool TopDUContext::deleting() const { ///@todo remove d_func()->m_deleting, not used any more return m_dynamicData->m_deleting; } QList TopDUContext::problems() const { ENSURE_CAN_READ if(m_local->m_sharedDataOwner) return m_local->m_problems + m_local->m_sharedDataOwner->m_local->m_problems; else return m_local->m_problems; } void TopDUContext::addProblem(const ProblemPointer& problem) { ENSURE_CAN_WRITE m_local->m_problems << problem; } void TopDUContext::clearProblems() { ENSURE_CAN_WRITE m_local->m_problems.clear(); } QVector TopDUContext::importers() const { ENSURE_CAN_READ return QVector::fromList( m_local->m_directImporters.toList() ); } QList TopDUContext::loadedImporters() const { ENSURE_CAN_READ return m_local->m_directImporters.toList(); } QVector TopDUContext::importedParentContexts() const { ENSURE_CAN_READ return DUContext::importedParentContexts(); } bool TopDUContext::imports(const DUContext * origin, const SimpleCursor& position) const { return importsPrivate(origin, position); } bool TopDUContext::importsPrivate(const DUContext * origin, const SimpleCursor& position) const { Q_UNUSED(position); if( const TopDUContext* top = dynamic_cast(origin) ) { QMutexLocker lock(&importStructureMutex); bool ret = recursiveImportIndices().contains(IndexedTopDUContext(const_cast(top))); if(top == this) Q_ASSERT(ret); return ret; } else { //Cannot import a non top-context return false; } } void TopDUContext::clearImportedParentContexts() { if(usingImportsCache()) { d_func_dynamic()->m_importsCache = IndexedRecursiveImports(); d_func_dynamic()->m_importsCache.insert(IndexedTopDUContext(this)); } - + if(!m_local->m_sharedDataOwner) DUContext::clearImportedParentContexts(); else { foreach (const Import &parent, m_local->m_importedContexts) if( parent.context(0) ) removeImportedParentContext(parent.context(0)); Q_ASSERT(m_local->m_importedContexts.isEmpty()); } - + m_local->clearImportedContextsRecursively(); - + Q_ASSERT(m_local->m_recursiveImports.count() == 0); - + Q_ASSERT(m_local->m_indexedRecursiveImports.count() == 1); - + Q_ASSERT(imports(this, SimpleCursor::invalid())); } void TopDUContext::addImportedParentContext(DUContext* context, const SimpleCursor& position, bool anonymous, bool temporary) { if(context == this) return; - + if(!dynamic_cast(context)) { //We cannot do this, because of the extended way we treat top-context imports. kDebug() << "tried to import a non top-context into a top-context. This is not possible."; return; } - + if(!m_local->m_sharedDataOwner) //Always make the contexts anonymous, because we care about importers in TopDUContextLocalPrivate - DUContext::addImportedParentContext(context, position, anonymous, temporary); - + DUContext::addImportedParentContext(context, position, anonymous, temporary); + m_local->addImportedContextRecursively(static_cast(context), temporary, true); } void TopDUContext::removeImportedParentContext(DUContext* context) { if(!m_local->m_sharedDataOwner) DUContext::removeImportedParentContext(context); - + m_local->removeImportedContextRecursively(static_cast(context), true); } void TopDUContext::addImportedParentContexts(const QList >& contexts, bool temporary) { typedef QPair Pair; foreach(const Pair &pair, contexts) addImportedParentContext(pair.first, pair.second, false, temporary); } void TopDUContext::removeImportedParentContexts(const QList& contexts) { foreach(TopDUContext* context, contexts) DUContext::removeImportedParentContext(context); if(!m_local->m_sharedDataOwner) m_local->removeImportedContextsRecursively(contexts, true); } /// Returns true if this object is registered in the du-chain. If it is not, all sub-objects(context, declarations, etc.) bool TopDUContext::inDuChain() const { return m_local->m_inDuChain; } /// This flag is only used by DUChain, never change it from outside. void TopDUContext::setInDuChain(bool b) { m_local->m_inDuChain = b; } TopDUContext::Flags TopDUContext::flags() const { return d_func()->m_flags; } void TopDUContext::setFlags(Flags f) { d_func_dynamic()->m_flags = f; } bool TopDUContext::isOnDisk() const { ///@todo Change this to releasingToDisk, and only enable it while saving a top-context to disk. return m_dynamicData->isOnDisk(); } IndexedString TopDUContext::language() const { return d_func()->m_language; } void TopDUContext::setLanguage(IndexedString language) { d_func_dynamic()->m_language = language; } void TopDUContext::clearUsedDeclarationIndices() { ENSURE_CAN_WRITE for(unsigned int a = 0; a < d_func()->m_usedDeclarationIdsSize(); ++a) DUChain::uses()->removeUse(d_func()->m_usedDeclarationIds()[a], this); d_func_dynamic()->m_usedDeclarationIdsList().clear(); } Declaration* TopDUContext::usedDeclarationForIndex(unsigned int declarationIndex) const { ENSURE_CAN_READ if(declarationIndex & (1<<31)) { //We use the highest bit to mark direct indices into the local declarations declarationIndex &= (0xffffffff - (1<<31)); //unset the highest bit return m_dynamicData->getDeclarationForIndex(declarationIndex); }else if(declarationIndex < d_func()->m_usedDeclarationIdsSize()) return d_func()->m_usedDeclarationIds()[declarationIndex].getDeclaration(this); else return 0; } int TopDUContext::indexForUsedDeclaration(Declaration* declaration, bool create) { if(create) { ENSURE_CAN_WRITE }else{ ENSURE_CAN_READ } - + if(!declaration) return std::numeric_limits::max(); - + if(declaration->topContext() == this && !declaration->inSymbolTable()) { uint index = declaration->ownIndex(); Q_ASSERT(!(index & (1<<31))); return (int)(index | (1<<31)); //We don't put context-local declarations into the list, that's a waste. We just use the mark them with the highest bit. } - + DeclarationId id(declaration->id()); int index = -1; - + uint size = d_func()->m_usedDeclarationIdsSize(); const DeclarationId* ids = d_func()->m_usedDeclarationIds(); - + for(unsigned int a = 0; a < size; ++a) if(ids[a] == id) { index = a; break; } if(index != -1) return index; if(!create) return std::numeric_limits::max(); d_func_dynamic()->m_usedDeclarationIdsList().append(id); if(declaration->topContext() != this) DUChain::uses()->addUse(id, this); return d_func()->m_usedDeclarationIdsSize()-1; } QList allSmartUses(TopDUContext* context, Declaration* declaration) { QList ret; int declarationIndex = context->indexForUsedDeclaration(declaration, false); if(declarationIndex == std::numeric_limits::max()) return ret; return allSmartUses(context, declarationIndex); } QList allUses(TopDUContext* context, Declaration* declaration, bool noEmptyRanges) { QList ret; int declarationIndex = context->indexForUsedDeclaration(declaration, false); if(declarationIndex == std::numeric_limits::max()) return ret; return allUses(context, declarationIndex, noEmptyRanges); } ///@todo move this kind of caching into the symbol-table TopDUContext::Cache::Cache(TopDUContextPointer context) : d(new CacheData(context)) { DUChainWriteLocker lock(DUChain::lock()); if(d->context) d->context->m_local->m_threadCaches.insert(QThread::currentThreadId(), d); } TopDUContext::Cache::~Cache() { DUChainWriteLocker lock(DUChain::lock()); if(d->context && d->context->m_local->m_threadCaches[QThread::currentThreadId()] == d) d->context->m_local->m_threadCaches.remove(QThread::currentThreadId()); delete d; } IndexedString TopDUContext::url() const { return d_func()->m_url; } } // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/topducontextdynamicdata.cpp b/language/duchain/topducontextdynamicdata.cpp index 6146febc6..94a96eb23 100644 --- a/language/duchain/topducontextdynamicdata.cpp +++ b/language/duchain/topducontextdynamicdata.cpp @@ -1,691 +1,692 @@ /* This is part of KDevelop 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 "topducontextdynamicdata.h" #include #include #include #include #include "declaration.h" #include "declarationdata.h" #include "ducontext.h" #include "topducontext.h" #include "topducontextdata.h" #include "ducontextdata.h" #include "duchainregister.h" #include "repositories/itemrepository.h" //#define DEBUG_DATA_INFO using namespace KDevelop; QMutex KDevelop::TopDUContextDynamicData::m_temporaryDataMutex(QMutex::Recursive); void saveDUChainItem(QList& data, DUChainBase& item, uint& totalDataOffset) { - + if(!item.d_func()->classId) { //If this triggers, you have probably created an own DUChainBase based class, but haven't called setClassId(this) in the constructor. kError() << QString("no class-id set for data attached to a declaration of type %1").arg(typeid(item).name()); Q_ASSERT(0); } - + int size = DUChainItemSystem::self().dynamicSize(*item.d_func()); - + if(data.back().first.size() - int(data.back().second) < size) //Create a new data item data.append( qMakePair(QByteArray(size > 10000 ? size : 10000, 0), 0u) ); - + uint pos = data.back().second; data.back().second += size; totalDataOffset += size; - + DUChainBaseData& target(*((DUChainBaseData*)(data.back().first.constData() + pos))); - + if(item.d_func()->isDynamic()) { //Change from dynamic data to constant data - + DUChainItemSystem::self().copy(*item.d_func(), target, true); Q_ASSERT(!target.isDynamic()); item.setData(&target); }else{ //Just copy the data into another place, expensive copy constructors are not needed memcpy(&target, item.d_func(), size); item.setData(&target, false); } - + Q_ASSERT(!item.d_func()->isDynamic()); } const char* KDevelop::TopDUContextDynamicData::pointerInData(const QList< KDevelop::ArrayWithPosition >& data, uint totalOffset) { for(int a = 0; a < data.size(); ++a) { if(totalOffset < data[a].second) return data[a].first.constData() + totalOffset; totalOffset -= data[a].second; } Q_ASSERT(0); //Offset doesn't exist in the data return 0; } void TopDUContextDynamicData::verifyDataInfo(const ItemDataInfo& info, const QList& data) { #ifdef DEBUG_DATA_INFO DUChainBaseData* item = (DUChainBaseData*)(pointerInData(data, info.dataOffset)); int size = DUChainItemSystem::self().dynamicSize(*item); Q_ASSERT(size); #endif } TopDUContextDynamicData::ItemDataInfo TopDUContextDynamicData::writeDataInfo(const ItemDataInfo& info, const QList& oldData, uint& totalDataOffset) { ItemDataInfo ret(info); Q_ASSERT(info.dataOffset); DUChainBaseData* data = (DUChainBaseData*)(pointerInData(oldData, info.dataOffset)); int size = DUChainItemSystem::self().dynamicSize(*data); Q_ASSERT(size); - + if(m_data.back().first.size() - int(m_data.back().second) < size) //Create a new m_data item m_data.append( qMakePair(QByteArray(size > 10000 ? size : 10000, 0), 0u) ); - + ret.dataOffset = totalDataOffset; - + uint pos = m_data.back().second; m_data.back().second += size; totalDataOffset += size; - + DUChainBaseData& target(*((DUChainBaseData*)(m_data.back().first.constData() + pos))); memcpy(&target, data, size); - + verifyDataInfo(ret, m_data); - + return ret; } TopDUContextDynamicData::TopDUContextDynamicData(TopDUContext* topContext) : m_topContext(topContext), m_fastContexts(0), m_fastContextsSize(0), m_fastDeclarations(0), m_fastDeclarationsSize(0), m_onDisk(false), m_dataLoaded(true), m_deleting(false) { } void KDevelop::TopDUContextDynamicData::clearContextsAndDeclartions() { //Due to template specialization it's possible that a declaration is not reachable through the normal context structure. //For that reason we have to check here, and delete all remaining declarations. for(int a = 0; a < m_temporaryContexts.size(); ++a) delete m_temporaryContexts[a]; - + for(int a = 0; a < m_temporaryDeclarations.size(); ++a) delete m_temporaryDeclarations[a]; - + for(int a = 0; a < m_contexts.size(); ++a) delete m_contexts[a]; - + for(int a = 0; a < m_declarations.size(); ++a) delete m_declarations[a]; } TopDUContextDynamicData::~TopDUContextDynamicData() { clearContextsAndDeclartions(); } QList TopDUContextDynamicData::loadImporters(uint topContextIndex) { QList ret; - + QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size - + //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(readValue); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); FOREACH_FUNCTION(IndexedDUContext importer, topData->m_importers) ret << importer; } - + return ret; } QList TopDUContextDynamicData::loadImports(uint topContextIndex) { QList ret; - + QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size - + //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(readValue); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); FOREACH_FUNCTION(DUContext::Import import, topData->m_importedContexts) ret << import.indexedContext(); } - + return ret; } IndexedString TopDUContextDynamicData::loadUrl(uint topContextIndex) { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size - + //We only read the most needed stuff, not the whole top-context data QByteArray data = file.read(sizeof(TopDUContextData)); const TopDUContextData* topData = (const TopDUContextData*)data.constData(); Q_ASSERT(topData->m_url.isEmpty() || topData->m_url.index() >> 16); return topData->m_url; } - + return IndexedString(); } void TopDUContextDynamicData::loadData() const { //This function has to be protected by an additional mutex, since it can be triggered from multiple threads at the same time static QMutex mutex; QMutexLocker lock(&mutex); if(m_dataLoaded) return; - + Q_ASSERT(!m_dataLoaded); Q_ASSERT(m_data.isEmpty()); Q_ASSERT(m_contextDataOffsets.isEmpty()); Q_ASSERT(m_declarations.isEmpty()); - + QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); - + QString fileName = baseDir + '/' + QString("%1").arg(m_topContext->ownIndex()); QFile file(fileName); bool open = file.open(QIODevice::ReadOnly); Q_ASSERT(open); Q_ASSERT(file.size()); - + //Skip the offsets, we're already read them //Skip top-context data uint readValue; file.read((char*)&readValue, sizeof(uint)); file.seek(readValue + file.pos()); - + file.read((char*)&readValue, sizeof(uint)); m_contextDataOffsets.resize(readValue); file.read((char*)m_contextDataOffsets.data(), sizeof(ItemDataInfo) * m_contextDataOffsets.size()); file.read((char*)&readValue, sizeof(uint)); m_declarationDataOffsets.resize(readValue); file.read((char*)m_declarationDataOffsets.data(), sizeof(ItemDataInfo) * m_declarationDataOffsets.size()); - + QByteArray data = file.readAll(); - + m_data.append(qMakePair(data, (uint)data.size())); - + //Fill with zeroes for now, will be initialized on-demand m_contexts.resize(m_contextDataOffsets.size()); m_fastContexts = m_contexts.data(); m_fastContextsSize = m_contexts.size(); - + m_declarations.resize(m_declarationDataOffsets.size()); m_fastDeclarations = m_declarations.data(); m_fastDeclarationsSize = m_declarations.size(); m_dataLoaded = true; } TopDUContext* TopDUContextDynamicData::load(uint topContextIndex) { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); - + QString fileName = baseDir + '/' + QString("%1").arg(topContextIndex); QFile file(fileName); if(file.open(QIODevice::ReadOnly)) { if(file.size() == 0) { kWarning() << "Top-context file is empty" << fileName; return 0; } QVector contextDataOffsets; QVector declarationDataOffsets; uint readValue; file.read((char*)&readValue, sizeof(uint)); //now readValue is filled with the top-context data size QByteArray topContextData = file.read(readValue); - + DUChainBaseData* topData = (DUChainBaseData*)topContextData.constData(); IndexedString language = static_cast(topData)->m_language; if(!language.isEmpty()) { ///@todo Load the language if it isn't loaded yet, problem: We're possibly not in the foreground thread! } TopDUContext* ret = dynamic_cast(DUChainItemSystem::self().create(topData)); if(!ret) { kWarning() << "Cannot load a top-context, the requered language-support is probably not loaded"; return 0; } - + //Disable the updating flag on loading. Sometimes it may be set when the context was saved while it was updated ret->setFlags( (TopDUContext::Flags) (ret->flags() & (~TopDUContext::UpdatingContext)) ); TopDUContextDynamicData& target(*ret->m_dynamicData); - + // kDebug() << "loaded" << ret->url().str() << ret->ownIndex() << "import-count:" << ret->importedParentContexts().size() << ret->d_func()->m_importedContextsSize(); - + target.m_data.clear(); target.m_dataLoaded = false; target.m_onDisk = true; ret->rebuildDynamicData(0, topContextIndex); target.m_topContextData.append(qMakePair(topContextData, (uint)0)); - + // kDebug() << "loaded" << ret->url().str() << ret->ownIndex() << "import-count:" << ret->importedParentContexts().size() << ret->d_func()->m_importedContextsSize(); - + return ret; }else{ kWarning() << "Cannot open top-context for reading:" << fileName; return 0; } } bool TopDUContextDynamicData::isOnDisk() const { return m_onDisk; } void TopDUContextDynamicData::deleteOnDisk() { if(!isOnDisk()) return; - + if(!m_dataLoaded) loadData(); - + for(int a = 0; a < m_fastContextsSize; ++a) if(m_fastContexts[a]) m_fastContexts[a]->makeDynamic(); - + for(int a = 0; a < m_fastDeclarationsSize; ++a) if(m_fastDeclarations[a]) m_fastDeclarations[a]->makeDynamic(); m_topContext->makeDynamic(); - + m_onDisk = false; - + bool successfullyRemoved = QFile::remove(filePath()); Q_ASSERT(successfullyRemoved); } QString KDevelop::TopDUContextDynamicData::filePath() const { QString baseDir = globalItemRepositoryRegistry().path() + "/topcontexts"; KStandardDirs::makeDir(baseDir); return baseDir + '/' + QString("%1").arg(m_topContext->ownIndex()); } void TopDUContextDynamicData::store() { // kDebug() << "storing" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); - + bool contentDataChanged = false; - + if(m_onDisk) { //Check if something has changed. If nothing has changed, don't store to disk. bool someThingChanged = false; if(m_topContext->d_ptr->m_dynamic) someThingChanged = true; - + for(int a = 0; a < m_fastContextsSize; ++a) { if(m_fastContexts[a] && m_fastContexts[a]->d_ptr->m_dynamic) { someThingChanged = true; contentDataChanged = true; } - + if(contentDataChanged) break; } for(int a = 0; a < m_fastDeclarationsSize; ++a) { if(m_fastDeclarations[a] && m_fastDeclarations[a]->d_ptr->m_dynamic) { someThingChanged = true; contentDataChanged = true; } - + if(contentDataChanged) break; } if(!someThingChanged) return; }else{ contentDataChanged = true; } - - + + ///@todo Save the meta-data into a repository, and only the actual content data into a file. /// This will make saving+loading more efficient, and will reduce the disk-usage. /// Then we also won't need to load the data if only the meta-data changed. if(!m_dataLoaded) loadData(); - + QFile file(filePath()); if(file.open(QIODevice::WriteOnly)) { - + file.resize(0); - + m_topContext->makeDynamic(); m_topContextData.clear(); Q_ASSERT(m_topContext->d_func()->m_ownIndex == m_topContext->ownIndex()); uint topContextDataSize = DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()); m_topContextData.append( qMakePair(QByteArray(DUChainItemSystem::self().dynamicSize(*m_topContext->d_func()), topContextDataSize), (uint)0) ); uint actualTopContextDataSize = 0; - + if(contentDataChanged) { //We don't need these structures any more, since we have loaded all the declarations/contexts, and m_data //will be reset which these structures pointed into //Load all lazy declarations/contexts QList oldData = m_data; //Keep the old data alive until everything is stored into a new data structure - + m_data.clear(); uint newDataSize = 0; foreach(const ArrayWithPosition &array, oldData) newDataSize += array.second; - + if(newDataSize < 10000) newDataSize = 10000; - + //We always put 1 byte to the front, so we don't have zero data-offsets, since those are used for "invalid". uint currentDataOffset = 1; m_data.append( qMakePair(QByteArray(newDataSize, 0), currentDataOffset) ); - + QVector oldContextDataOffsets = m_contextDataOffsets; QVector oldDeclarationDataOffsets = m_declarationDataOffsets; m_contextDataOffsets.clear(); m_declarationDataOffsets.clear(); - + for(int a = 0; a < m_fastContextsSize; ++a) { if(!m_fastContexts[a]) { if(oldContextDataOffsets.size() > a && oldContextDataOffsets[a].dataOffset) { //Directly copy the old data range into the new data m_contextDataOffsets << writeDataInfo(oldContextDataOffsets[a], oldData, currentDataOffset); }else{ m_contextDataOffsets << ItemDataInfo(); } } else { m_contextDataOffsets << ItemDataInfo(currentDataOffset, LocalIndexedDUContext(m_fastContexts[a]->parentContext()).localIndex()); saveDUChainItem(m_data, *m_fastContexts[a], currentDataOffset); verifyDataInfo(m_contextDataOffsets.back(), m_data); Q_ASSERT(!m_fastContexts[a]->d_func()->isDynamic()); } } - + for(int a = 0; a < m_fastDeclarationsSize; ++a) { - + if(!m_fastDeclarations[a]) { if(oldDeclarationDataOffsets.size() > a && oldDeclarationDataOffsets[a].dataOffset) { //Directly copy the old data range into the new data m_declarationDataOffsets << writeDataInfo(oldDeclarationDataOffsets[a], oldData, currentDataOffset); }else{ m_declarationDataOffsets << ItemDataInfo(); } } else { m_declarationDataOffsets << ItemDataInfo(currentDataOffset, LocalIndexedDUContext(m_fastDeclarations[a]->context()).localIndex()); saveDUChainItem(m_data, *m_fastDeclarations[a], currentDataOffset); verifyDataInfo(m_declarationDataOffsets.back(), m_data); Q_ASSERT(!m_fastDeclarations[a]->d_func()->isDynamic()); } } } for(int a = 0; a < m_fastContextsSize; ++a) if(m_fastContexts[a]) Q_ASSERT(!m_fastContexts[a]->d_func()->isDynamic()); - + for(int a = 0; a < m_fastDeclarationsSize; ++a) if(m_fastDeclarations[a]) Q_ASSERT(!m_fastDeclarations[a]->d_func()->isDynamic()); saveDUChainItem(m_topContextData, *m_topContext, actualTopContextDataSize); Q_ASSERT(actualTopContextDataSize == topContextDataSize); Q_ASSERT(m_topContextData.size() == 1); Q_ASSERT(!m_topContext->d_func()->isDynamic()); - + file.write((char*)&topContextDataSize, sizeof(uint)); foreach(const ArrayWithPosition& pos, m_topContextData) file.write(pos.first.constData(), pos.second); - + uint writeValue = m_contextDataOffsets.size(); file.write((char*)&writeValue, sizeof(uint)); file.write((char*)m_contextDataOffsets.data(), sizeof(ItemDataInfo) * m_contextDataOffsets.size()); writeValue = m_declarationDataOffsets.size(); file.write((char*)&writeValue, sizeof(uint)); file.write((char*)m_declarationDataOffsets.data(), sizeof(ItemDataInfo) * m_declarationDataOffsets.size()); - + foreach(const ArrayWithPosition& pos, m_data) file.write(pos.first.constData(), pos.second); - + m_onDisk = true; - - Q_ASSERT(file.size() != 0); - + + if (file.size() == 0) + kWarning() << "Saving zero size top ducontext data"; + }else{ kWarning() << "Cannot open top-context for writing"; } // kDebug() << "stored" << m_topContext->url().str() << m_topContext->ownIndex() << "import-count:" << m_topContext->importedParentContexts().size(); } uint TopDUContextDynamicData::allocateDeclarationIndex(Declaration* decl, bool temporary) { if(!m_dataLoaded) loadData(); if(!temporary) { m_declarations.append(decl); m_fastDeclarations = m_declarations.data(); m_fastDeclarationsSize = m_declarations.size(); return m_fastDeclarationsSize; }else{ QMutexLocker lock(&m_temporaryDataMutex); m_temporaryDeclarations.append(decl); return 0x0fffffff - m_temporaryDeclarations.size(); //We always keep the highest bit at zero } } bool TopDUContextDynamicData::isDeclarationForIndexLoaded(uint index) const { if(!m_dataLoaded) return false; if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) return false; return (bool)m_fastDeclarations[index-1]; }else{ return true; } } bool TopDUContextDynamicData::isContextForIndexLoaded(uint index) const { if(!m_dataLoaded) return false; if(index < (0x0fffffff/2)) { if(index == 0) return true; if(index > uint(m_fastContextsSize)) return false; return (bool)m_fastContexts[index-1]; }else{ return true; } } Declaration* TopDUContextDynamicData::getDeclarationForIndex(uint index) const { if(!m_dataLoaded) loadData(); - + if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) { kWarning() << "index out of bounds:" << index << "count:" << m_fastDeclarationsSize; return 0; } else { uint realIndex = index-1; if(!m_fastDeclarations[realIndex] && realIndex < (uint)m_declarationDataOffsets.size() && m_declarationDataOffsets[realIndex].dataOffset) { m_fastDeclarations[realIndex] = static_cast(DUChainItemSystem::self().create((DUChainBaseData*)(pointerInData(m_data, m_declarationDataOffsets[realIndex].dataOffset)))); if(!m_fastDeclarations[realIndex]) { //When this happens, the declaration has not been registered correctly. //We can stop here, because else we will get crashes later. kError() << "Failed to load declaration with identity" << ((DUChainBaseData*)(pointerInData(m_data, m_declarationDataOffsets[realIndex].dataOffset)))->classId; Q_ASSERT(0); }else{ DUContext* context = getContextForIndex(m_declarationDataOffsets[realIndex].parentContext); Q_ASSERT(context); //If this triggers, the context has been deleted without deleting its contained declarations m_fastDeclarations[realIndex]->rebuildDynamicData(context, index); } } - + return m_fastDeclarations[realIndex]; } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryDeclarations.size())) return 0; else return m_temporaryDeclarations[index-1]; } } void TopDUContextDynamicData::clearDeclarationIndex(Declaration* decl) { if(!m_dataLoaded) loadData(); - + uint index = decl->m_indexInTopContext; if(index < (0x0fffffff/2)) { if(index == 0 || index > uint(m_fastDeclarationsSize)) return; else { Q_ASSERT(m_fastDeclarations[index-1] == decl); m_fastDeclarations[index-1] = 0; - + if(index-1 < (uint)m_declarationDataOffsets.size()) m_declarationDataOffsets[index-1] = ItemDataInfo(); } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryDeclarations.size())) return; else { Q_ASSERT(m_temporaryDeclarations[index-1] == decl); m_temporaryDeclarations[index-1] = 0; } } } uint TopDUContextDynamicData::allocateContextIndex(DUContext* decl, bool temporary) { if(!m_dataLoaded) loadData(); - + if(!temporary) { m_contexts.append(decl); m_fastContexts = m_contexts.data(); m_fastContextsSize = m_contexts.size(); return m_fastContextsSize; }else{ QMutexLocker lock(&m_temporaryDataMutex); m_temporaryContexts.append(decl); return 0x0fffffff - m_temporaryContexts.size(); //We always keep the highest bit at zero } } bool TopDUContextDynamicData::isTemporaryContextIndex(uint index) const { return !(index < (0x0fffffff/2)); } bool TopDUContextDynamicData::isTemporaryDeclarationIndex(uint index) const { return !(index < (0x0fffffff/2)); } DUContext* TopDUContextDynamicData::getContextForIndex(uint index) const { - + if(!m_dataLoaded) loadData(); - + if(index < (0x0fffffff/2)) { if(index == 0) return m_topContext; if(index > uint(m_fastContextsSize)) { Q_ASSERT(0); return 0; } else { uint realIndex = index-1; DUContext** fastContextsPos = (m_fastContexts + realIndex); if(*fastContextsPos) //Shortcut, because this is the most common case return *fastContextsPos; - + if(!*fastContextsPos && realIndex < (uint)m_contextDataOffsets.size() && m_contextDataOffsets[realIndex].dataOffset) { //Construct the context, and eventuall its parent first *fastContextsPos = dynamic_cast(DUChainItemSystem::self().create((DUChainBaseData*)(pointerInData(m_data, m_contextDataOffsets[realIndex].dataOffset)))); if(!*fastContextsPos) { //When this happens, the declaration has not been registered correctly. //We can stop here, because else we will get crashes later. kError() << "Failed to load declaration with identity" << ((DUChainBaseData*)(pointerInData(m_data, m_contextDataOffsets[realIndex].dataOffset)))->classId; }else{ (*fastContextsPos)->rebuildDynamicData(getContextForIndex(m_contextDataOffsets[realIndex].parentContext), index); } } - + return *fastContextsPos; } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryContexts.size())) return 0; else return m_temporaryContexts[index-1]; } } void TopDUContextDynamicData::clearContextIndex(DUContext* decl) { - + if(!m_dataLoaded) loadData(); - + uint index = decl->m_dynamicData->m_indexInTopContext; if(index < (0x0fffffff/2)) { - + if(index == 0 || index > uint(m_fastContextsSize)) return; else { Q_ASSERT(m_fastContexts[index-1] == decl); m_fastContexts[index-1] = 0; - + if(index-1 < (uint)m_contextDataOffsets.size()) m_contextDataOffsets[index-1] = ItemDataInfo(); } }else{ QMutexLocker lock(&m_temporaryDataMutex); index = 0x0fffffff - index; //We always keep the highest bit at zero if(index == 0 || index > uint(m_temporaryContexts.size())) return; else { Q_ASSERT(m_temporaryContexts[index-1] == decl); m_temporaryContexts[index-1] = 0; } } } diff --git a/language/duchain/topducontextutils.h b/language/duchain/topducontextutils.h new file mode 100644 index 000000000..77a907ae3 --- /dev/null +++ b/language/duchain/topducontextutils.h @@ -0,0 +1,45 @@ +/* This file is part of KDevelop + 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 TOPDUCONTEXTUTILS_H +#define TOPDUCONTEXTUTILS_H + +#include "topducontext.h" + +namespace KDevelop +{ + +struct KDEVPLATFORMLANGUAGE_EXPORT TopDUContext::DeclarationChecker +{ + DeclarationChecker(const TopDUContext* _top, const SimpleCursor& _position, const AbstractType::Ptr& _dataType, DUContext::SearchFlags _flags, KDevVarLengthArray* _createVisibleCache = 0); + bool operator()(IndexedDeclaration dec) const; + + mutable KDevVarLengthArray* createVisibleCache; + const TopDUContext* top; + const TopDUContextData* topDFunc; + const SimpleCursor& position; + const AbstractType::Ptr& dataType; + DUContext::SearchFlags flags; +}; + +} + +#endif // TOPDUCONTEXTUTILS_H + +// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/language/duchain/types/abstracttype.h b/language/duchain/types/abstracttype.h index baf87dcaa..aa7c7764c 100644 --- a/language/duchain/types/abstracttype.h +++ b/language/duchain/types/abstracttype.h @@ -1,293 +1,285 @@ /* This file is part of KDevelop Copyright 2006 Roberto Raggi Copyright 2006-2008 Hamish Rodda 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 ABSTRACTTYPE_H #define ABSTRACTTYPE_H #include #include "typepointer.h" #include "../../languageexport.h" namespace KDevelop { class AbstractTypeData; class IndexedType; class TypeVisitor; class TypeExchanger; #define TYPE_DECLARE_DATA(Class) \ inline Class##Data* d_func_dynamic() { makeDynamic(); return reinterpret_cast(d_ptr); } \ inline const Class##Data* d_func() const { return reinterpret_cast(d_ptr); } #define TYPE_D(Class) const Class##Data * const d = d_func() #define TYPE_D_DYNAMIC(Class) Class##Data * const d = d_func_dynamic() /** * \brief Base class for all types. * * The AbstractType class is a base class from which all types derive. It features: * - mechanisms for visiting types * - toString() feature * - equivalence feature * - cloning of types, and * - hashing and indexing * * Type classes are created in a way that allows storing them in memory or on disk * efficiently. They are classes which can store arbitrary lists immediately after their * private data structures in memory (thus enabling them to be mmapped or memcopied), * or being "dynamic" where you use exactly the same class and same access functions, * but the list data is stored in a temporary KDevVarLengthArray from a central repository, * until we save it back to the static memory-region again. * * When creating an own type, you must: * - Implement equals(..), clone(), hash() * - Add an enumerator "Identity" that contains an arbitrary unique identity value of the type * - Add a typedef "BaseType" that specifies the base type, which must be a type that also follows these rules(@todo) * - Register the type in a source-file using REGISTER_TYPE(..), @see typeregister.h * - Add a typedef "Data", that contains the actual data of the type using the mechanisms described in appendedlist.h. * That data type must follow the same inheritance chain as the type itself, so it must be based on BaseType::Data. * @see AbstractTypeData * - When creating a data object in a constructor, you must call setTypeClassId() on that data to mark it. * * Every type can have only one other type as base-class, * but it can have additional base-classes that are not a direct part of the type-system(@see IdentifiedType). * * \sa appendedlist.h */ class KDEVPLATFORMLANGUAGE_EXPORT AbstractType : public TypeShared { public: typedef TypePtr Ptr; /** * An enumeration of common modifiers for data types. * If you have any language-specific modifiers that don't belong here, * you can add them at/after LanguageSpecificModifier * @warning Think twice what information you store into the type-system. * The type-system should store information that is shared among many declarations, * and attributes of specific Declarations like public/private should be stored in * the Declarations themselves, not in the type-system. */ enum CommonModifiers { NoModifiers = 0, ConstModifier = 1 << 0, VolatileModifier = 1 << 1, TransientModifier = 1 << 2, - FinalModifier = 1 << 3,///@todo remove, is a property of a declaration, not of its type - AbstractModifier = 1 << 4,///@todo remove, is a property of a declaration, not of its type - NativeModifier = 1 << 5,///@todo remove, is a property of a declaration, not of its type - SynchronizedModifier = 1 << 6,///@todo remove, is a property of a declaration, not of its type - StrictFPModifier = 1 << 7, - NewModifier = 1 << 8, - SealedModifier = 1 << 9, - StaticModifier = 1 << 10,///@todo remove, is a property of a declaration, not of its type - ReadonlyModifier = 1 << 11,///@todo remove, is a property of a declaration, not of its type - OverrideModifier = 1 << 12,///@todo remove, is a property of a declaration, not of its type - UnsafeModifier = 1 << 13, - FixedModifier = 1 << 14, - ShortModifier = 1 << 15, - LongModifier = 1 << 16, - LongLongModifier = 1 << 17, - SignedModifier = 1 << 18, - UnsignedModifier = 1 << 19, - LanguageSpecificModifier = 1 << 20 //TODO make this support 64 bit values + NewModifier = 1 << 3, + SealedModifier = 1 << 4, + UnsafeModifier = 1 << 5, + FixedModifier = 1 << 6, + ShortModifier = 1 << 7, + LongModifier = 1 << 8, + LongLongModifier = 1 << 9, + SignedModifier = 1 << 10, + UnsignedModifier = 1 << 11, + LanguageSpecificModifier = 1 << 12 //TODO make this support 64 bit values }; /// Constructor. AbstractType(); /// Constructor from data. AbstractType(AbstractTypeData& dd); /// Destructor. virtual ~AbstractType (); /** * Access the type modifiers * * \returns the type's modifiers. */ quint64 modifiers() const; /** * Set the type's modifiers. * * \param modifiers modifiers of this type. */ void setModifiers(quint64 modifiers); /** * Visitor method. Called by TypeVisitor to visit the type heirachy. * * \param v visitor which is calling this function. */ void accept(TypeVisitor *v) const; /** * Convenience visitor method which can be called with a null type. * * \param type type to visit, may be null. * \param v visitor which is visiting the given \a type */ static void acceptType(AbstractType::Ptr type, TypeVisitor *v); /** * Returns this type as a string, preferably the same as it is expressed in the code. * * \return this type as a string */ virtual QString toString() const; ///Must always be called before anything in the data pointer is changed! ///If it's not called beforehand, the type-repository gets corrupted void makeDynamic(); ///Should return whether this type's content equals the given one ///Since this is used by the type-repository, it must compare ALL members of the data type. virtual bool equals(const AbstractType* rhs) const; /** * Should create a clone of the source-type, with as much data copied as possible without breaking the du-chain. * */ virtual AbstractType* clone() const = 0; /** * A hash-value that should have the following properties: * - When two types match on equals(), it should be same. * - When two types don't match on equals(), it should be different with a high probability. * */ virtual uint hash() const; ///This can also be called on zero types, those can then be reconstructed from the zero index IndexedType indexed() const; /// Enumeration of major data types. enum WhichType { TypeAbstract /**< an abstract type */, TypeIntegral /**< an integral */, TypePointer /**< a pointer*/, TypeReference /**< a reference */, TypeFunction /**< a function */, TypeStructure /**< a structure */, TypeArray /**< an array */, TypeDelayed /**< a delayed type */, TypeEnumeration /**< an enumeration type */, TypeEnumerator /**< an enumerator type */ }; /** * Determine which data type this abstract type represents. * * \returns the data type represented by this type. */ virtual WhichType whichType() const; enum { Identity = 1 }; /** * Should, like accept0, be implemented by all types that hold references to other types. * * \todo document function * */ virtual void exchangeTypes( TypeExchanger* exchanger ); /** * Method to create copies of internal type data. You must use this to create the internal * data instances in copy constructors. It is needed, because it may need to allocate more memory * for appended lists. * * \param rhs data to copy * \returns copy of the data */ template static DataType& copyData(const DataType& rhs) { size_t size; if(!rhs.m_dynamic) size = sizeof(DataType); //Create a dynamic data instance else size = rhs.dynamicSize(); //Create a constant data instance, that holds all the data embedded. return *new (new char[size]) DataType(rhs); } template static typename Type::Data& copyDataDownwards(const typename Type::Data& rhs) { typename Type::Data& ret(copyData(rhs)); ret.template setTypeClassId(); return ret; } /** * Method to create internal data structures. Use this in normal constructors. * * \returns the internal data structure */ template static DataType& createData() { return *new (new char[sizeof(DataType)]) DataType(); } typedef AbstractTypeData Data; protected: /** * Visitor method, reimplement to allow visiting of types. * * \param v visitor which is visiting. */ virtual void accept0 (TypeVisitor *v) const = 0; /// toString() function which can provide \a spaceOnLeft rather than on right if desired. QString toString(bool spaceOnLeft) const; AbstractTypeData* d_ptr; TYPE_DECLARE_DATA(AbstractType) friend class AbstractTypeDataRequest; private: AbstractType(const AbstractType& rhs); }; template uint qHash(const TypePtr& type) { return (uint)((size_t)type.unsafeData()); } /** * You can use these instead of dynamic_cast, for basic types it has better performance because it checks the whichType() member */ template inline To fastCast(AbstractType* from) { return dynamic_cast(from); } template inline const To fastCast(const AbstractType* from) { return const_cast(fastCast(const_cast(from))); //Hack so we don't need to define the functions twice, once for const, and once for not const } } #endif // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on