diff --git a/bin/test/data/log/test_log.txt b/bin/test/data/log/test_log.txt index 66447c43..5ef4bcdf 100644 --- a/bin/test/data/log/test_log.txt +++ b/bin/test/data/log/test_log.txt @@ -1,37 +1,11 @@ ConfigManager.cpp ERROR: value path/to/nowhere is not present in config. -Storage.cpp INFO: class: A -Storage.cpp INFO: method: A::A -Storage.cpp INFO: global usage: A::A -> A::count -Storage.cpp INFO: method: A::A -Storage.cpp INFO: global usage: A::A -> A::count -Storage.cpp INFO: method: A::getCount -Storage.cpp INFO: global usage: A::getCount -> A::count -Storage.cpp INFO: method: A::process -Storage.cpp INFO: field: A::count -Storage.cpp INFO: class: B -Storage.cpp INFO: inheritance: B : A -Storage.cpp INFO: method: B::process -Storage.cpp INFO: override: A::process -> B::process < 0:0 0:0> -Storage.cpp INFO: type usage: B::process -> int -Storage.cpp INFO: function: main -Storage.cpp INFO: type usage: main -> B -Storage.cpp INFO: call: main -> B::B -Storage.cpp INFO: call: main -> A::getCount -SearchMatch.cpp INFO: -1 matches for "main": -474 main - ^^^^ - -SearchMatch.cpp INFO: -1 matches for "main": -474 main - ^^^^ - -SearchMatch.cpp INFO: -1 matches for "A::A": -237 A::A - ^^^^ - +Token.cpp ERROR: Location Id was not referenced by this Token. +Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class +Edge.cpp ERROR: Nodes are not plain copies. +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Storage.cpp INFO: file: input.cc < 0:0 0:0> Storage.cpp INFO: class: A Storage.cpp INFO: method: A::A Storage.cpp INFO: global usage: A::A -> A::count @@ -48,12 +22,40 @@ Storage.cpp INFO: function: main Storage.cpp INFO: type usage: main -> B Storage.cpp INFO: call: main -> B::B Storage.cpp INFO: call: main -> A::getCount -Token.cpp ERROR: Location Id was not referenced by this Token. -Node.cpp WARNING: Cannot change NodeType after it was already set from namespace to class -Edge.cpp ERROR: Nodes are not plain copies. -Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined -Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined -Edge.cpp ERROR: Edge usage can't go from Node undefined to Node undefined +Storage.cpp INFO: file: input.cc < 0:0 0:0> +Storage.cpp INFO: class: A +Storage.cpp INFO: method: A::A +Storage.cpp INFO: global usage: A::A -> A::count +Storage.cpp INFO: method: A::A +Storage.cpp INFO: global usage: A::A -> A::count +Storage.cpp INFO: method: A::getCount +Storage.cpp INFO: global usage: A::getCount -> A::count +Storage.cpp INFO: method: A::process +Storage.cpp INFO: field: A::count +Storage.cpp INFO: class: B +Storage.cpp INFO: inheritance: B : A +Storage.cpp INFO: method: B::process +Storage.cpp INFO: override: A::process -> B::process < 0:0 0:0> +Storage.cpp INFO: type usage: B::process -> int +Storage.cpp INFO: function: _main_ +Storage.cpp INFO: type usage: _main_ -> B +Storage.cpp INFO: call: _main_ -> B::B +Storage.cpp INFO: call: _main_ -> A::getCount +SearchMatch.cpp INFO: +1 matches for "_main_": +755 _main_ + ^^^^^^ + +SearchMatch.cpp INFO: +1 matches for "_main_": +755 _main_ + ^^^^^^ + +SearchMatch.cpp INFO: +1 matches for "A::A": +237 A::A + ^^^^ + Settings.cpp WARNING: File for Settings not found. ConfigManager.cpp ERROR: value Bool is not present in config. ConfigManager.cpp ERROR: value Int is not present in config. @@ -69,7 +71,6 @@ ConfigManager.cpp ERROR: value Int is not present in config. ConfigManager.cpp ERROR: value Float is not present in config. ConfigManager.cpp ERROR: value String is not present in config. ConfigManager.cpp ERROR: value NewBool is not present in config. -Graph.cpp ERROR: Can't remove member edge, without removing the child node. Storage.cpp INFO: typedef: type -> int Storage.cpp INFO: class: Class Storage.cpp INFO: struct: Struct @@ -125,6 +126,16 @@ Storage.cpp INFO: call: main -> isTrue Storage.cpp INFO: function: isTrue Storage.cpp INFO: function: main Storage.cpp INFO: call: main -> isTrue +Storage.cpp INFO: file: file.h < 0:0 0:0> +Storage.cpp INFO: file: file.h < 0:0 0:0> +Storage.cpp INFO: file: file.cpp < 0:0 0:0> +Storage.cpp INFO: include: file.h +Storage.cpp INFO: file: f.h < 0:0 0:0> +Storage.cpp INFO: file: file.h < 0:0 0:0> +Storage.cpp INFO: file: file.cpp < 0:0 0:0> +Storage.cpp INFO: include: f.h +Storage.cpp INFO: include: file.h +Graph.cpp ERROR: Can't remove member edge, without removing the child node. TextAccess.cpp WARNING: Index 'firstLine' has to be lower or equal index 'lastLine', is 3 > 2 TextAccess.cpp WARNING: Tried to access index 10. Maximum index is 8 TextAccess.cpp WARNING: Tried to access index 10. Maximum index is 8 diff --git a/src/app/qt/view/graphElements/QtGraphNode.cpp b/src/app/qt/view/graphElements/QtGraphNode.cpp index 08592bdd..a1c23581 100644 --- a/src/app/qt/view/graphElements/QtGraphNode.cpp +++ b/src/app/qt/view/graphElements/QtGraphNode.cpp @@ -49,6 +49,7 @@ QFont QtGraphNode::getFontForNodeType(Node::NodeType type) case Node::NODE_ENUM: case Node::NODE_TYPEDEF: case Node::NODE_TEMPLATE_PARAMETER_TYPE: + case Node::NODE_FILE: font.setPixelSize(14); break; @@ -321,6 +322,7 @@ void QtGraphNode::setStyle() case Node::NODE_ENUM: case Node::NODE_TYPEDEF: case Node::NODE_TEMPLATE_PARAMETER_TYPE: + case Node::NODE_FILE: if (m_isActive) { font.setWeight(QFont::Bold); diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index ee215dd8..784c86f7 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -17,6 +17,8 @@ add_files( data/parser/cxx/CxxDiagnosticConsumer.h data/parser/cxx/CxxParser.cpp data/parser/cxx/CxxParser.h + data/parser/cxx/PreprocessorCallbacks.cpp + data/parser/cxx/PreprocessorCallbacks.h data/parser/cxx/utilityCxx.cpp data/parser/cxx/utilityCxx.h ) diff --git a/src/lib/Project.cpp b/src/lib/Project.cpp index 72eb5ee5..01514b87 100644 --- a/src/lib/Project.cpp +++ b/src/lib/Project.cpp @@ -84,33 +84,42 @@ void Project::parseCode() { std::vector includePaths; includePaths.push_back(sourcePath); + + // TODO: move this creation to another place (after projectsettings have been loaded) if (!m_fileManager) { std::vector sourcePaths; sourcePaths.push_back(sourcePath); + std::vector sourceExtensions; sourceExtensions.push_back(".cpp"); sourceExtensions.push_back(".cc"); + std::vector includeExtensions; includeExtensions.push_back(".h"); includeExtensions.push_back(".hpp"); - m_fileManager = std::make_shared(sourcePaths, includePaths, sourceExtensions, includeExtensions); // todo: move this creation to another place (after projectsettings have been loaded) + + m_fileManager = std::make_shared(sourcePaths, includePaths, sourceExtensions, includeExtensions); } m_fileManager->fetchFilePaths(); - std::vector addedFilePaths = m_fileManager->getAddedFilePaths(); - std::vector updatedFilePaths = m_fileManager->getUpdatedFilePaths(); - std::vector removedFilePaths = m_fileManager->getRemovedFilePaths(); + std::set addedFilePaths = m_fileManager->getAddedFilePaths(); + std::set updatedFilePaths = m_fileManager->getUpdatedFilePaths(); + std::set removedFilePaths = m_fileManager->getRemovedFilePaths(); + + std::set dependingFilePaths; + dependingFilePaths = m_storage->getDependingFilePathsAndRemoveFileNodes(updatedFilePaths); + updatedFilePaths.insert(dependingFilePaths.begin(), dependingFilePaths.end()); + + dependingFilePaths = m_storage->getDependingFilePathsAndRemoveFileNodes(removedFilePaths); + updatedFilePaths.insert(dependingFilePaths.begin(), dependingFilePaths.end()); - m_storage->clearFileData(addedFilePaths); m_storage->clearFileData(updatedFilePaths); m_storage->clearFileData(removedFilePaths); - std::vector filesToParse = addedFilePaths; - for (std::string updatedFilePath: updatedFilePaths) - { - filesToParse.push_back(updatedFilePath); - } + std::vector filesToParse; + filesToParse.insert(filesToParse.end(), addedFilePaths.begin(), addedFilePaths.end()); + filesToParse.insert(filesToParse.end(), updatedFilePaths.begin(), updatedFilePaths.end()); if (filesToParse.size() == 0) { diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index 57f85297..18dd93da 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -96,8 +96,7 @@ CodeView* CodeController::getView() std::vector CodeController::getSnippetsForActiveTokenIds( const std::vector& ids, Id declarationId ) const { - std::vector locationIds = m_graphAccess->getLocationIdsForTokenIds(ids); - TokenLocationCollection collection = m_locationAccess->getTokenLocationsForLocationIds(locationIds); + TokenLocationCollection collection = m_locationAccess->getTokenLocationsForTokenIds(ids); std::vector snippets; diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 238181cb..ad7745a1 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -467,6 +467,7 @@ GraphController::Margins GraphController::getMarginsForDummyNode(DummyNode& node case Node::NODE_ENUM: case Node::NODE_TYPEDEF: case Node::NODE_TEMPLATE_PARAMETER_TYPE: + case Node::NODE_FILE: if (node.subNodes.size()) { margins.left = margins.right = 15; diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 3c4c0c9e..176e2754 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -2,6 +2,7 @@ #include "utility/logging/logging.h" #include "utility/utilityString.h" +#include "utility/file/FileSystem.h" #include "data/graph/filter/GraphFilterConductor.h" #include "data/graph/token_component/TokenComponentConst.h" @@ -41,7 +42,7 @@ void Storage::clear() m_errorLocationCollection.clear(); } -void Storage::clearFileData(const std::vector& filePaths) +void Storage::clearFileData(const std::set& filePaths) { for (const std::string& filePath : filePaths) { @@ -98,6 +99,36 @@ void Storage::clearFileData(const std::vector& filePaths) } } +std::set Storage::getDependingFilePathsAndRemoveFileNodes(const std::set& filePaths) +{ + std::set dependingFilePaths; + + for (const std::string& filePath : filePaths) + { + SearchNode* searchNode = m_tokenIndex.getNode(filePath); + if (!searchNode || searchNode->getTokenIds().size() != 1) + { + continue; + } + + Node* fileNode = m_graph.getNodeById(searchNode->getFirstTokenId()); + if (!fileNode->isType(Node::NODE_FILE)) + { + LOG_ERROR("Node is not of type file."); + continue; + } + + addDependingFilePathsAndRemoveFileNodesRecursive(fileNode, &dependingFilePaths); + } + + for (const std::string& path : filePaths) + { + dependingFilePaths.erase(path); + } + + return dependingFilePaths; +} + void Storage::logGraph() const { LOG_INFO_STREAM(<< '\n' << m_graph); @@ -605,6 +636,27 @@ Id Storage::onTemplateFunctionSpecializationParsed( return specializedFunctionNode->getId(); } +Id Storage::onFileParsed(const std::string& filePath) +{ + log("file", filePath, ParseLocation()); + + Node* fileNode = addNodeHierarchy(Node::NODE_FILE, std::vector(1, FileSystem::absoluteFilePath(filePath))); + return fileNode->getId(); +} + +Id Storage::onFileIncludeParsed(const ParseLocation& location, const std::string& filePath, const std::string& includedPath) +{ + log("include", includedPath, location); + + Node* fileNode = addNodeHierarchy(Node::NODE_FILE, std::vector(1, FileSystem::absoluteFilePath(filePath))); + Node* includedFileNode = addNodeHierarchy(Node::NODE_FILE, std::vector(1, FileSystem::absoluteFilePath(includedPath))); + + Edge* edge = m_graph.createEdge(Edge::EDGE_INCLUDE, fileNode, includedFileNode); + addTokenLocation(edge, location); + + return edge->getId(); +} + Id Storage::getIdForNodeWithName(const std::string& fullName) const { SearchNode* node = m_tokenIndex.getNode(fullName); @@ -823,24 +875,6 @@ std::vector Storage::getActiveTokenIdsForLocationId(Id locationId) const return ret; } -std::vector Storage::getLocationIdsForTokenIds(const std::vector& tokenIds) const -{ - std::vector ret; - - for (Id tokenId : tokenIds) - { - Token* token = m_graph.getTokenById(tokenId); - if (!token) - { - continue; - } - - ret.insert(ret.end(), token->getLocationIds().begin(), token->getLocationIds().end()); - } - - return ret; -} - std::vector Storage::getTokenIdsForQuery(std::string query) const { QueryTree tree(query); @@ -854,17 +888,43 @@ std::vector Storage::getTokenIdsForQuery(std::string query) const return outGraph.getTokenIds(); } -TokenLocationCollection Storage::getTokenLocationsForLocationIds(const std::vector& locationIds) const +TokenLocationCollection Storage::getTokenLocationsForTokenIds(const std::vector& tokenIds) const { TokenLocationCollection ret; - for (Id locationId: locationIds) + for (Id tokenId : tokenIds) { - TokenLocation* location = m_locationCollection.findTokenLocationById(locationId); - if (location->getOtherTokenLocation()) + Token* token = m_graph.getTokenById(tokenId); + if (!token) { - ret.addTokenLocationAsPlainCopy(location); - ret.addTokenLocationAsPlainCopy(location->getOtherTokenLocation()); + continue; + } + + if (token->isNode() && dynamic_cast(token)->isType(Node::NODE_FILE)) + { + TokenLocationFile* locationFile = + m_locationCollection.findTokenLocationFileByPath(dynamic_cast(token)->getFullName()); + if (locationFile) + { + locationFile->forEachTokenLocation( + [&ret](TokenLocation* tokenLocation) -> void + { + ret.addTokenLocationAsPlainCopy(tokenLocation); + } + ); + } + } + else + { + for (Id locationId: token->getLocationIds()) + { + TokenLocation* location = m_locationCollection.findTokenLocationById(locationId); + if (location->getOtherTokenLocation()) + { + ret.addTokenLocationAsPlainCopy(location); + ret.addTokenLocationAsPlainCopy(location->getOtherTokenLocation()); + } + } } } @@ -1177,6 +1237,39 @@ bool Storage::getQuerySearchResults(const std::string& query, const std::string& return true; } +void Storage::addDependingFilePathsAndRemoveFileNodesRecursive(Node* fileNode, std::set* filePaths) +{ + bool inserted = filePaths->insert(fileNode->getFullName()).second; + if (!inserted) + { + return; + } + + while (true) + { + Edge* edge = fileNode->findEdgeOfType(Edge::EDGE_INCLUDE); + if (!edge) + { + break; + } + + Node* dependantNode = nullptr; + if (edge->getTo() == fileNode) + { + dependantNode = edge->getFrom(); + } + + m_graph.removeEdge(edge); + + if (dependantNode) + { + addDependingFilePathsAndRemoveFileNodesRecursive(dependantNode, filePaths); + } + } + + removeNodeIfUnreferenced(fileNode); +} + void Storage::removeNodeIfUnreferenced(Node* node) { Id tokenId = node->getId(); diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 7370b34a..45203d1f 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -23,7 +23,8 @@ public: virtual ~Storage(); void clear(); - void clearFileData(const std::vector& filePaths); + void clearFileData(const std::set& filePaths); + std::set getDependingFilePathsAndRemoveFileNodes(const std::set& filePaths); void logGraph() const; void logLocations() const; @@ -100,6 +101,10 @@ public: virtual Id onTemplateFunctionSpecializationParsed( const ParseLocation& location, const ParseFunction specializedFunction, const ParseFunction templateFunction); + virtual Id onFileParsed(const std::string& filePath); + virtual Id onFileIncludeParsed( + const ParseLocation& location, const std::string& filePath, const std::string& includedPath); + // GraphAccess implementation virtual Id getIdForNodeWithName(const std::string& fullName) const; virtual std::string getNameForNodeWithId(Id id) const; @@ -110,12 +115,11 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const; virtual std::vector getActiveTokenIdsForLocationId(Id locationId) const; - virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const; virtual std::vector getTokenIdsForQuery(std::string query) const; // LocationAccess implementation - virtual TokenLocationCollection getTokenLocationsForLocationIds(const std::vector& locationIds) const; + virtual TokenLocationCollection getTokenLocationsForTokenIds(const std::vector& tokenIds) const; virtual TokenLocationFile getTokenLocationsForFile(const std::string& filePath) const; virtual TokenLocationFile getTokenLocationsForLinesInFile( const std::string& filePath, uint firstLineNumber, uint lastLineNumber @@ -143,6 +147,7 @@ private: bool getQuerySearchResults(const std::string& query, const std::string& word, SearchResults* results) const; + void addDependingFilePathsAndRemoveFileNodesRecursive(Node* fileNode, std::set* filePaths); void removeNodeIfUnreferenced(Node* node); void log(std::string type, std::string str, const ParseLocation& location) const; diff --git a/src/lib/data/access/GraphAccess.h b/src/lib/data/access/GraphAccess.h index d0adb659..de0717ad 100644 --- a/src/lib/data/access/GraphAccess.h +++ b/src/lib/data/access/GraphAccess.h @@ -23,7 +23,6 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const = 0; virtual std::vector getActiveTokenIdsForLocationId(Id locationId) const = 0; - virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const = 0; virtual std::vector getTokenIdsForQuery(std::string query) const = 0; }; diff --git a/src/lib/data/access/GraphAccessProxy.cpp b/src/lib/data/access/GraphAccessProxy.cpp index 29d468f7..3784b104 100644 --- a/src/lib/data/access/GraphAccessProxy.cpp +++ b/src/lib/data/access/GraphAccessProxy.cpp @@ -89,16 +89,6 @@ std::vector GraphAccessProxy::getActiveTokenIdsForLocationId(Id locationId) return std::vector(); } -std::vector GraphAccessProxy::getLocationIdsForTokenIds(const std::vector& tokenIds) const -{ - if (hasSubject()) - { - return m_subject->getLocationIdsForTokenIds(tokenIds); - } - - return std::vector(); -} - std::vector GraphAccessProxy::getTokenIdsForQuery(std::string query) const { if (hasSubject()) diff --git a/src/lib/data/access/GraphAccessProxy.h b/src/lib/data/access/GraphAccessProxy.h index 311c6424..b45fa41f 100644 --- a/src/lib/data/access/GraphAccessProxy.h +++ b/src/lib/data/access/GraphAccessProxy.h @@ -22,7 +22,6 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const; virtual std::vector getActiveTokenIdsForLocationId(Id locationId) const; - virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const; virtual std::vector getTokenIdsForQuery(std::string query) const; diff --git a/src/lib/data/access/LocationAccess.h b/src/lib/data/access/LocationAccess.h index a0f7ea08..2ca2f3a4 100644 --- a/src/lib/data/access/LocationAccess.h +++ b/src/lib/data/access/LocationAccess.h @@ -13,7 +13,7 @@ class LocationAccess { public: virtual ~LocationAccess(); - virtual TokenLocationCollection getTokenLocationsForLocationIds(const std::vector& locationIds) const = 0; + virtual TokenLocationCollection getTokenLocationsForTokenIds(const std::vector& tokenIds) const = 0; virtual TokenLocationFile getTokenLocationsForFile(const std::string& filePath) const = 0; virtual TokenLocationFile getTokenLocationsForLinesInFile( const std::string& filePath, uint firstLineNumber, uint lastLineNumber diff --git a/src/lib/data/access/LocationAccessProxy.cpp b/src/lib/data/access/LocationAccessProxy.cpp index c067abad..97d05e53 100644 --- a/src/lib/data/access/LocationAccessProxy.cpp +++ b/src/lib/data/access/LocationAccessProxy.cpp @@ -29,11 +29,11 @@ void LocationAccessProxy::setSubject(LocationAccess* subject) m_subject = subject; } -TokenLocationCollection LocationAccessProxy::getTokenLocationsForLocationIds(const std::vector& locationIds) const +TokenLocationCollection LocationAccessProxy::getTokenLocationsForTokenIds(const std::vector& tokenIds) const { if (hasSubject()) { - return m_subject->getTokenLocationsForLocationIds(locationIds); + return m_subject->getTokenLocationsForTokenIds(tokenIds); } return TokenLocationCollection(); diff --git a/src/lib/data/access/LocationAccessProxy.h b/src/lib/data/access/LocationAccessProxy.h index 8b6b6445..e87caf36 100644 --- a/src/lib/data/access/LocationAccessProxy.h +++ b/src/lib/data/access/LocationAccessProxy.h @@ -13,7 +13,7 @@ public: void setSubject(LocationAccess* subject); // LocationAccess implementation - virtual TokenLocationCollection getTokenLocationsForLocationIds(const std::vector& locationIds) const; + virtual TokenLocationCollection getTokenLocationsForTokenIds(const std::vector& tokenIds) const; virtual TokenLocationFile getTokenLocationsForFile(const std::string& filePath) const; virtual TokenLocationFile getTokenLocationsForLinesInFile( const std::string& filePath, uint firstLineNumber, uint lastLineNumber diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp index 8f04ee90..cec35d4c 100644 --- a/src/lib/data/graph/Edge.cpp +++ b/src/lib/data/graph/Edge.cpp @@ -141,6 +141,8 @@ std::string Edge::getTypeString(EdgeType type) const return "template default argument"; case EDGE_TEMPLATE_SPECIALIZATION_OF: return "template specialization"; + case EDGE_INCLUDE: + return "include"; case EDGE_AGGREGATION: return "aggregation"; } @@ -270,6 +272,13 @@ bool Edge::checkType() const } return true; + case EDGE_INCLUDE: + if (!m_from->isType(Node::NODE_FILE) || !m_to->isType(Node::NODE_FILE)) + { + break; + } + return true; + case EDGE_AGGREGATION: if (!m_from->isType(typeMask | variableMask | functionMask) || !m_to->isType(typeMask | variableMask | functionMask)) { diff --git a/src/lib/data/graph/Edge.h b/src/lib/data/graph/Edge.h index e438fe7c..22f5be5d 100644 --- a/src/lib/data/graph/Edge.h +++ b/src/lib/data/graph/Edge.h @@ -32,7 +32,9 @@ public: EDGE_TEMPLATE_DEFAULT_ARGUMENT_OF = 0x1000, EDGE_TEMPLATE_SPECIALIZATION_OF = 0x2000, - EDGE_AGGREGATION = 0x4000 + EDGE_INCLUDE = 0x4000, + + EDGE_AGGREGATION = 0x8000 }; Edge(EdgeType type, Node* from, Node* to); diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp index 3f16198c..0b6e75f4 100644 --- a/src/lib/data/graph/Node.cpp +++ b/src/lib/data/graph/Node.cpp @@ -2,12 +2,14 @@ #include +#include "utility/file/FileSystem.h" +#include "utility/logging/logging.h" + #include "data/graph/token_component/TokenComponentAbstraction.h" #include "data/graph/token_component/TokenComponentConst.h" #include "data/graph/token_component/TokenComponentName.h" #include "data/graph/token_component/TokenComponentStatic.h" #include "data/graph/token_component/TokenComponentSignature.h" -#include "utility/logging/logging.h" Node::Node(NodeType type, std::shared_ptr nameComponent) : m_type(type) @@ -50,7 +52,14 @@ bool Node::isType(NodeTypeMask mask) const std::string Node::getName() const { - return m_nameComponent->getName(); + if (isType(NODE_FILE)) + { + return FileSystem::fileName(m_nameComponent->getName()); + } + else + { + return m_nameComponent->getName(); + } } std::string Node::getFullName() const @@ -342,6 +351,8 @@ std::string Node::getTypeString(NodeType type) const return "typedef"; case NODE_TEMPLATE_PARAMETER_TYPE: return "template parameter type"; + case NODE_FILE: + return "file"; } return ""; } diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h index 9234dcef..34a7aff9 100644 --- a/src/lib/data/graph/Node.h +++ b/src/lib/data/graph/Node.h @@ -37,7 +37,9 @@ public: NODE_ENUM = 0x800, NODE_ENUM_CONSTANT = 0x1000, NODE_TYPEDEF = 0x2000, - NODE_TEMPLATE_PARAMETER_TYPE = 0x4000 + NODE_TEMPLATE_PARAMETER_TYPE = 0x4000, + + NODE_FILE = 0x8000 }; Node(NodeType type, std::shared_ptr nameComponent); diff --git a/src/lib/data/graph/filter/GraphFilterConductor.cpp b/src/lib/data/graph/filter/GraphFilterConductor.cpp index 023aa3b3..4dae0882 100644 --- a/src/lib/data/graph/filter/GraphFilterConductor.cpp +++ b/src/lib/data/graph/filter/GraphFilterConductor.cpp @@ -174,6 +174,10 @@ void GraphFilterConductor::filterCommandNode(const QueryCommand* node, const Fil GraphFilterCommandInheritance(false).apply(in, out); break; + case QueryCommand::COMMAND_FILE: + GraphFilterCommandNodeType(Node::NODE_FILE).apply(in, out); + break; + default: LOG_ERROR_STREAM(<< "QueryCommand not supported: " << node->getType()); GraphFilter().apply(in, out); diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h index b90ce635..80ede54b 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -116,6 +116,10 @@ public: const ParseFunction function) = 0; virtual Id onTemplateFunctionSpecializationParsed( const ParseLocation& location, const ParseFunction specializedFunction, const ParseFunction templateFunction) = 0; + + virtual Id onFileParsed(const std::string& filePath) = 0; + virtual Id onFileIncludeParsed( + const ParseLocation& location, const std::string& filePath, const std::string& includedPath) = 0; }; #endif // PARSER_CLIENT_H diff --git a/src/lib/data/parser/cxx/ASTAction.cpp b/src/lib/data/parser/cxx/ASTAction.cpp index db37e4e1..97b28bb0 100644 --- a/src/lib/data/parser/cxx/ASTAction.cpp +++ b/src/lib/data/parser/cxx/ASTAction.cpp @@ -1,5 +1,9 @@ #include "data/parser/cxx/ASTAction.h" +#include "clang/Lex/Preprocessor.h" + +#include "data/parser/cxx/PreprocessorCallbacks.h" + ASTAction::ASTAction(ParserClient* client, FileManager* fileManager) : m_client(client) , m_fileManager(fileManager) @@ -14,3 +18,14 @@ std::unique_ptr ASTAction::CreateASTConsumer(clang::Compiler { return std::unique_ptr(new ASTConsumer(&compiler.getASTContext(), m_client, m_fileManager)); } + +bool ASTAction::BeginSourceFileAction(clang::CompilerInstance& compiler, llvm::StringRef filePath) +{ + m_client->onFileParsed(filePath.str()); + + clang::Preprocessor& preprocessor = compiler.getPreprocessor(); + preprocessor.addPPCallbacks( + llvm::make_unique(compiler.getSourceManager(), m_client, m_fileManager)); + + return true; +} diff --git a/src/lib/data/parser/cxx/ASTAction.h b/src/lib/data/parser/cxx/ASTAction.h index 60ab5690..8fc4a333 100644 --- a/src/lib/data/parser/cxx/ASTAction.h +++ b/src/lib/data/parser/cxx/ASTAction.h @@ -15,6 +15,8 @@ public: virtual std::unique_ptr CreateASTConsumer(clang::CompilerInstance& compiler, llvm::StringRef inFile); + virtual bool BeginSourceFileAction(clang::CompilerInstance& compiler, llvm::StringRef filePath); + private: ParserClient* m_client; FileManager* m_fileManager; diff --git a/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp b/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp new file mode 100644 index 00000000..9a4bf7bc --- /dev/null +++ b/src/lib/data/parser/cxx/PreprocessorCallbacks.cpp @@ -0,0 +1,52 @@ +#include "data/parser/cxx/PreprocessorCallbacks.h" + +#include "utility/file/FileManager.h" +#include "data/parser/ParserClient.h" +#include "data/parser/ParseLocation.h" + +PreprocessorCallbacks::PreprocessorCallbacks( + clang::SourceManager& sourceManager, ParserClient* client, FileManager* fileManager +) + : m_sourceManager(sourceManager) + , m_client(client) + , m_fileManager(fileManager) +{ +} + +void PreprocessorCallbacks::InclusionDirective( + clang::SourceLocation hashLocation, const clang::Token& includeToken, llvm::StringRef fileName, bool isAngled, + clang::CharSourceRange fileNameRange, const clang::FileEntry* fileEntry, llvm::StringRef searchPath, + llvm::StringRef relativePath, const clang::Module* imported +){ + const clang::FileEntry* baseFileEntry = m_sourceManager.getFileEntryForID(m_sourceManager.getFileID(hashLocation)); + if (fileEntry && baseFileEntry) + { + std::string baseFilePath = baseFileEntry->getName(); + std::string filePath = fileEntry->getName(); + + if (m_fileManager->hasFilePath(baseFilePath) && m_fileManager->hasFilePath(filePath)) + { + m_client->onFileIncludeParsed( + getParseLocation(fileNameRange.getAsRange()), baseFileEntry->getName(), fileEntry->getName()); + } + } +} + +ParseLocation PreprocessorCallbacks::getParseLocation(const clang::SourceRange& sourceRange) const +{ + if (sourceRange.isInvalid()) + { + return ParseLocation(); + } + + const clang::PresumedLoc& presumedBegin = m_sourceManager.getPresumedLoc(sourceRange.getBegin(), false); + const clang::PresumedLoc& presumedEnd = m_sourceManager.getPresumedLoc(sourceRange.getEnd(), false); + + return ParseLocation( + presumedBegin.getFilename(), + presumedBegin.getLine(), + presumedBegin.getColumn(), + presumedEnd.getLine(), + presumedEnd.getColumn() - 1 + ); +} diff --git a/src/lib/data/parser/cxx/PreprocessorCallbacks.h b/src/lib/data/parser/cxx/PreprocessorCallbacks.h new file mode 100644 index 00000000..e266224d --- /dev/null +++ b/src/lib/data/parser/cxx/PreprocessorCallbacks.h @@ -0,0 +1,31 @@ +#ifndef PREPROCESSOR_CALLBACKS_H +#define PREPROCESSOR_CALLBACKS_H + +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/PPCallbacks.h" + +class FileManager; +class ParserClient; + +struct ParseLocation; + +class PreprocessorCallbacks + : public clang::PPCallbacks +{ +public: + explicit PreprocessorCallbacks(clang::SourceManager& sourceManager, ParserClient* client, FileManager* fileManager); + + virtual void InclusionDirective( + clang::SourceLocation hashLocation, const clang::Token& includeToken, llvm::StringRef fileName, bool isAngled, + clang::CharSourceRange fileNameRange, const clang::FileEntry* fileEntry, llvm::StringRef searchPath, + llvm::StringRef relativePath, const clang::Module* imported); + +private: + ParseLocation getParseLocation(const clang::SourceRange& sourceRange) const; + + const clang::SourceManager& m_sourceManager; + ParserClient* m_client; + FileManager* m_fileManager; +}; + +#endif // PREPROCESSOR_CALLBACKS_H diff --git a/src/lib/data/query/QueryCommand.cpp b/src/lib/data/query/QueryCommand.cpp index 80955810..01f46d64 100644 --- a/src/lib/data/query/QueryCommand.cpp +++ b/src/lib/data/query/QueryCommand.cpp @@ -48,6 +48,8 @@ std::map QueryCommand::getCommandTypeMap commandMap.emplace("derived", COMMAND_SUB_CLASS); commandMap.emplace("subclass", COMMAND_SUB_CLASS); + commandMap.emplace("file", COMMAND_FILE); + return commandMap; } diff --git a/src/lib/data/query/QueryCommand.h b/src/lib/data/query/QueryCommand.h index cc011043..35fbe5e2 100644 --- a/src/lib/data/query/QueryCommand.h +++ b/src/lib/data/query/QueryCommand.h @@ -41,7 +41,9 @@ public: COMMAND_USAGE, COMMAND_SUPER_CLASS, - COMMAND_SUB_CLASS + COMMAND_SUB_CLASS, + + COMMAND_FILE }; static std::map getCommandTypeMap(); diff --git a/src/lib/utility/file/FileManager.cpp b/src/lib/utility/file/FileManager.cpp index 652a580a..32161202 100644 --- a/src/lib/utility/file/FileManager.cpp +++ b/src/lib/utility/file/FileManager.cpp @@ -4,7 +4,6 @@ #include #include "utility/file/FileSystem.h" -#include "ProjectSettings.h" FileManager::FileManager( std::vector sourcePaths, @@ -37,10 +36,9 @@ void FileManager::fetchFilePaths() m_updatedFiles.clear(); m_removedFiles.clear(); - std::set removedFileNames; for (std::map::iterator it = m_files.begin(); it != m_files.end(); it++) { - removedFileNames.insert(it->first); + m_removedFiles.insert(it->first); } std::vector, std::vector>> pathsExtensionsPairs; @@ -49,51 +47,52 @@ void FileManager::fetchFilePaths() for (size_t i = 0; i < pathsExtensionsPairs.size(); i++) { - std::vector fileInfos = FileSystem::getFileInfosFromDirectoryPaths(pathsExtensionsPairs[i].first, pathsExtensionsPairs[i].second); + std::vector fileInfos = + FileSystem::getFileInfosFromDirectoryPaths(pathsExtensionsPairs[i].first, pathsExtensionsPairs[i].second); + for (FileInfo fileInfo: fileInfos) { const std::string& filePath = fileInfo.path; std::map::iterator it = m_files.find(filePath); if (it != m_files.end()) { - removedFileNames.erase(filePath); + m_removedFiles.erase(filePath); if (fileInfo.lastWriteTime > it->second.lastWriteTime) { it->second.lastWriteTime = fileInfo.lastWriteTime; - m_updatedFiles.push_back(filePath); + m_updatedFiles.insert(fileInfo.path); } } else { m_files.insert(std::pair(filePath, fileInfo)); - m_addedFiles.push_back(filePath); + m_addedFiles.insert(filePath); } } } - for (std::set::iterator it = removedFileNames.begin(); it != removedFileNames.end(); it++) + for (const std::string filePath : m_removedFiles) { - m_files.erase(it->data()); - m_removedFiles.push_back(it->data()); + m_files.erase(filePath); } } -std::vector FileManager::getAddedFilePaths() const +std::set FileManager::getAddedFilePaths() const { return m_addedFiles; } -std::vector FileManager::getUpdatedFilePaths() const +std::set FileManager::getUpdatedFilePaths() const { return m_updatedFiles; } -std::vector FileManager::getRemovedFilePaths() const +std::set FileManager::getRemovedFilePaths() const { return m_removedFiles; } bool FileManager::hasFilePath(const std::string& filePath) const { - return (m_files.find(filePath) != m_files.end()); + return (m_files.find(FileSystem::absoluteFilePath(filePath)) != m_files.end()); } diff --git a/src/lib/utility/file/FileManager.h b/src/lib/utility/file/FileManager.h index aa715a10..fd899f54 100644 --- a/src/lib/utility/file/FileManager.h +++ b/src/lib/utility/file/FileManager.h @@ -1,7 +1,7 @@ #ifndef FILE_MANAGER_H #define FILE_MANAGER_H -#include +#include #include #include "FileInfo.h" @@ -19,9 +19,10 @@ public: void reset(); void fetchFilePaths(); - std::vector getAddedFilePaths() const; - std::vector getUpdatedFilePaths() const; - std::vector getRemovedFilePaths() const; + + std::set getAddedFilePaths() const; + std::set getUpdatedFilePaths() const; + std::set getRemovedFilePaths() const; virtual bool hasFilePath(const std::string& filePath) const; @@ -32,9 +33,9 @@ private: std::vector m_includeExtensions; std::map m_files; - std::vector m_addedFiles; - std::vector m_updatedFiles; - std::vector m_removedFiles; + std::set m_addedFiles; + std::set m_updatedFiles; + std::set m_removedFiles; }; #endif // FILE_MANAGER_H diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index 8cdb7f54..565b034b 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -1709,6 +1709,10 @@ public: TS_ASSERT_EQUALS(client.calls.size(), 2); TS_ASSERT_EQUALS(client.usages.size(), 3); TS_ASSERT_EQUALS(client.typeUses.size(), 8); + + TS_ASSERT_EQUALS(client.files.size(), 2); + TS_ASSERT_EQUALS(client.includes.size(), 1); + TS_ASSERT_EQUALS(client.includes[0], client.files[1] + " -> " + client.files[0]); } void test_cxx_parser_catches_error() @@ -1954,6 +1958,19 @@ private: return 0; } + virtual Id onFileParsed(const std::string& filePath) + { + files.push_back(filePath); + return 0; + } + + virtual Id onFileIncludeParsed( + const ParseLocation& location, const std::string& filePath, const std::string& includedPath) + { + includes.push_back(filePath + " -> " + includedPath); + return 0; + } + std::vector errors; std::vector typedefs; @@ -1977,6 +1994,9 @@ private: std::vector templateDefaultArgumentTypes; std::vector templateSpecializations; + std::vector files; + std::vector includes; + private: void addTypeUse(const ParseTypeUsage& use) { diff --git a/src/test/FileManagerTestSuite.h b/src/test/FileManagerTestSuite.h index ca9e93c0..e763f3d4 100644 --- a/src/test/FileManagerTestSuite.h +++ b/src/test/FileManagerTestSuite.h @@ -20,13 +20,9 @@ public: FileManager fm = FileManager(sourcePaths, includePaths, sourceExtensions, includeExtensions); - std::vector addedFilePaths = fm.getAddedFilePaths(); - std::vector updatedFilePaths = fm.getUpdatedFilePaths(); - std::vector removedFilePaths = fm.getRemovedFilePaths(); - - TS_ASSERT_EQUALS(addedFilePaths.size(), 0); - TS_ASSERT_EQUALS(updatedFilePaths.size(), 0); - TS_ASSERT_EQUALS(removedFilePaths.size(), 0); + TS_ASSERT_EQUALS(fm.getAddedFilePaths().size(), 0); + TS_ASSERT_EQUALS(fm.getUpdatedFilePaths().size(), 0); + TS_ASSERT_EQUALS(fm.getRemovedFilePaths().size(), 0); } void test_file_manager_has_added_file_paths_after_first_fetch() @@ -45,11 +41,7 @@ public: FileManager fm = FileManager(sourcePaths, includePaths, sourceExtensions, includeExtensions); fm.fetchFilePaths(); - std::vector addedFilePaths = fm.getAddedFilePaths(); - std::vector updatedFilePaths = fm.getUpdatedFilePaths(); - std::vector removedFilePaths = fm.getRemovedFilePaths(); - - TS_ASSERT_EQUALS(addedFilePaths.size(), 4); + TS_ASSERT_EQUALS(fm.getAddedFilePaths().size(), 4); } void test_file_manager_has_no_added_file_paths_after_second_fetch() @@ -69,11 +61,7 @@ public: fm.fetchFilePaths(); fm.fetchFilePaths(); - std::vector addedFilePaths = fm.getAddedFilePaths(); - std::vector updatedFilePaths = fm.getUpdatedFilePaths(); - std::vector removedFilePaths = fm.getRemovedFilePaths(); - - TS_ASSERT_EQUALS(addedFilePaths.size(), 0); + TS_ASSERT_EQUALS(fm.getAddedFilePaths().size(), 0); } void test_file_manager_has_updated_file_paths_after_second_fetch() @@ -99,12 +87,8 @@ public: fm.fetchFilePaths(); - std::vector addedFilePaths = fm.getAddedFilePaths(); - std::vector updatedFilePaths = fm.getUpdatedFilePaths(); - std::vector removedFilePaths = fm.getRemovedFilePaths(); - - TS_ASSERT_EQUALS(addedFilePaths.size(), 0); - TS_ASSERT_EQUALS(updatedFilePaths.size(), 1); - TS_ASSERT_EQUALS(removedFilePaths.size(), 0); + TS_ASSERT_EQUALS(fm.getAddedFilePaths().size(), 0); + TS_ASSERT_EQUALS(fm.getUpdatedFilePaths().size(), 1); + TS_ASSERT_EQUALS(fm.getRemovedFilePaths().size(), 0); } }; diff --git a/src/test/GraphFilterConductorTestSuite.h b/src/test/GraphFilterConductorTestSuite.h index 108ee899..84d11409 100644 --- a/src/test/GraphFilterConductorTestSuite.h +++ b/src/test/GraphFilterConductorTestSuite.h @@ -11,18 +11,18 @@ public: void test_token_query() { TS_ASSERT_EQUALS( - printedFilteredTestGraph("\"main\""), + printedFilteredTestGraph("\"_main_\""), - "1 nodes: function:main\n" + "1 nodes: function:_main_\n" "0 edges:\n" ); } void test_token_query_with_id() { - std::set ids = getIdsForNodeWithName("main"); + std::set ids = getIdsForNodeWithName("_main_"); std::stringstream ss; - ss << "\"main"; + ss << "\"_main_"; for (Id id : ids) { ss << ',' << id; @@ -30,16 +30,16 @@ public: ss << '"'; TS_ASSERT_EQUALS( - printedFilteredTestGraph(ss.str()), // "main," + printedFilteredTestGraph(ss.str()), // "_main_," - "1 nodes: function:main\n" + "1 nodes: function:_main_\n" "0 edges:\n" ); } void test_token_query_with_id_and_wrong_name_uses_id() { - std::set ids = getIdsForNodeWithName("main"); + std::set ids = getIdsForNodeWithName("_main_"); std::stringstream ss; ss << "\"hello"; for (Id id : ids) @@ -51,7 +51,7 @@ public: TS_ASSERT_EQUALS( printedFilteredTestGraph(ss.str()), // "hello," - "1 nodes: function:main\n" + "1 nodes: function:_main_\n" "0 edges:\n" ); } @@ -97,13 +97,12 @@ public: TS_ASSERT_EQUALS( printedFilteredTestGraph("!'method'"), - "7 nodes: " - "class:A field:A::count undefined_type:int undefined_type:void class:B function:main " - "undefined_function:B::B\n" - "14 edges: " - "child:A->A::count aggregation:A->int aggregation:A->void type_use:A::count->int inheritance:B->A " - "aggregation:B->void aggregation:A->B aggregation:B->int return_type:main->int type_usage:main->B " - "child:B->B::B call:main->B::B aggregation:main->B aggregation:main->A\n" + "8 nodes: " + "file:/Users/ebsi/Documents/masterproject/bin/test/input.cc class:A field:A::count undefined_type:int " + "undefined_type:void class:B function:_main_ undefined_function:B::B\n" + "14 edges: child:A->A::count aggregation:A->int aggregation:A->void type_use:A::count->int inheritance:B->A " + "aggregation:B->void aggregation:A->B aggregation:B->int return_type:_main_->int type_usage:_main_->B " + "child:B->B::B call:_main_->B::B aggregation:_main_->B aggregation:_main_->A\n" ); } @@ -219,7 +218,7 @@ private: " }\n" "};\n" "\n" - "int main()\n" + "int _main_()\n" "{\n" " B b;\n" "\n" diff --git a/src/test/StorageTestSuite.h b/src/test/StorageTestSuite.h index 601de9b3..b597cc81 100644 --- a/src/test/StorageTestSuite.h +++ b/src/test/StorageTestSuite.h @@ -573,7 +573,9 @@ public: TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 4); TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 3); - storage.clearFileData(std::vector(1, m_filePath)); + std::set files; + files.insert(m_filePath); + storage.clearFileData(files); TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 0); TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 0); @@ -602,7 +604,9 @@ public: TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 6); - storage.clearFileData(std::vector(1, "file.cpp")); + std::set files; + files.insert("file.cpp"); + storage.clearFileData(files); TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 3); TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 2); @@ -631,7 +635,9 @@ public: TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); - storage.clearFileData(std::vector(1, "file.h")); + std::set files; + files.insert("file.h"); + storage.clearFileData(files); TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 4); TS_ASSERT_EQUALS(storage.graph().getEdgeCount(), 3); @@ -660,9 +666,9 @@ public: TS_ASSERT_EQUALS(storage.tokenLocationCollection().getTokenLocations().size(), 9); TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 5); - std::vector filePaths; - filePaths.push_back("file.cpp"); - filePaths.push_back("file.h"); + std::set filePaths; + filePaths.insert("file.cpp"); + filePaths.insert("file.h"); storage.clearFileData(filePaths); TS_ASSERT_EQUALS(storage.graph().getNodeCount(), 0); @@ -671,6 +677,65 @@ public: TS_ASSERT_EQUALS(storage.searchIndex().getNodeCount(), 0); } + void test_storage_saves_file_nodes() + { + TestStorage storage; + + Id id = storage.onFileParsed("file.h"); + Node* node = storage.getNodeWithId(id); + + TS_ASSERT(node); + TS_ASSERT_EQUALS(node->getName(), "file.h"); + TS_ASSERT_EQUALS(node->getType(), Node::NODE_FILE); + } + + void test_storage_saves_include_edge() + { + TestStorage storage; + + storage.onFileParsed("file.h"); + storage.onFileParsed("file.cpp"); + Id id = storage.onFileIncludeParsed(validLocation(7), "file.cpp", "file.h"); + + Edge* edge = storage.getEdgeWithId(id); + TS_ASSERT(edge); + TS_ASSERT_EQUALS(edge->getType(), Edge::EDGE_INCLUDE); + + TS_ASSERT_EQUALS(edge->getFrom()->getName(), "file.cpp"); + TS_ASSERT_EQUALS(edge->getTo()->getName(), "file.h"); + + std::vector locations = storage.getLocationsForId(id); + TS_ASSERT_EQUALS(locations.size(), 1); + TS_ASSERT(isValidLocation(locations[0], 7)); + } + + void test_storage_finds_and_removes_depending_file_nodes() + { + TestStorage storage; + + Id id1 = storage.onFileParsed("f.h"); + Id id2 = storage.onFileParsed("file.h"); + Id id3 = storage.onFileParsed("file.cpp"); + Id id4 = storage.onFileIncludeParsed(validLocation(), "file.h", "f.h"); + Id id5 = storage.onFileIncludeParsed(validLocation(), "file.cpp", "file.h"); + + std::string name1 = storage.getNodeWithId(id2)->getFullName(); + std::string name2 = storage.getNodeWithId(id3)->getFullName(); + + std::set filePaths; + filePaths.insert(name1); + std::set dependingFilePaths = storage.getDependingFilePathsAndRemoveFileNodes(filePaths); + + TS_ASSERT_EQUALS(dependingFilePaths.size(), 1); + TS_ASSERT_EQUALS(*dependingFilePaths.begin(), name2); + + TS_ASSERT(storage.getNodeWithId(id1)); + TS_ASSERT(!storage.getNodeWithId(id2)); + TS_ASSERT(!storage.getNodeWithId(id3)); + TS_ASSERT(!storage.getEdgeWithId(id4)); + TS_ASSERT(!storage.getEdgeWithId(id5)); + } + private: class TestStorage : public Storage