From cf6d17584f566b0b54e22b7c9cc40c1fd4abb575 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Tue, 1 Jul 2014 10:49:12 +0200 Subject: [PATCH] data: Saving TokenLocations in Storage This change adds structures for saving TokenLocations in the Storage and improves the location derival in the ASTVisitor: * TokenLocation saves an Id of a Token. For each Token two TokenLocations are created for both start- and endpoint. They keep a reference to each other and know whether they are start or end. * TokenLocationLine saves all TokenLocations in a line and the lineNumber. * TokenLocationFile saves all TokenLocationLines of a specific file and the filePath. * TokenLocationCollection saves multiple TokenLocationFiles. * TokenLocation can derive it's lineNumber and filePath through references to first TokenLocationLine and from there to TokenLocationFile. --- src/lib/CMakeLists.txt | 15 +- src/lib/Project.cpp | 2 + src/lib/data/Storage.cpp | 49 +++++- src/lib/data/Storage.h | 10 +- src/lib/data/TextLocation.cpp | 24 --- src/lib/data/TextLocation.h | 28 --- src/lib/data/TextLocationFile.cpp | 10 -- src/lib/data/TextLocationFile.h | 22 --- src/lib/data/TextLocationLine.cpp | 18 -- src/lib/data/TextLocationLine.h | 28 --- src/lib/data/graph/Token.cpp | 29 +++- src/lib/data/graph/Token.h | 9 + src/lib/data/location/TokenLocation.cpp | 126 ++++++++++++++ src/lib/data/location/TokenLocation.h | 57 ++++++ .../data/location/TokenLocationCollection.cpp | 138 +++++++++++++++ .../data/location/TokenLocationCollection.h | 51 ++++++ src/lib/data/location/TokenLocationFile.cpp | 152 ++++++++++++++++ src/lib/data/location/TokenLocationFile.h | 48 ++++++ src/lib/data/location/TokenLocationLine.cpp | 113 ++++++++++++ src/lib/data/location/TokenLocationLine.h | 50 ++++++ src/lib/data/parser/ParseLocation.cpp | 14 +- src/lib/data/parser/ParseLocation.h | 13 +- src/lib/data/parser/cxx/ASTVisitor.cpp | 78 ++++----- src/lib/data/parser/cxx/ASTVisitor.h | 4 +- src/test/CMakeLists.txt | 1 + src/test/CxxParserTestSuite.h | 101 ++++++----- src/test/TokenLocationCollectionTestSuite.h | 162 ++++++++++++++++++ 27 files changed, 1109 insertions(+), 243 deletions(-) delete mode 100644 src/lib/data/TextLocation.cpp delete mode 100644 src/lib/data/TextLocation.h delete mode 100644 src/lib/data/TextLocationFile.cpp delete mode 100644 src/lib/data/TextLocationFile.h delete mode 100644 src/lib/data/TextLocationLine.cpp delete mode 100644 src/lib/data/TextLocationLine.h create mode 100644 src/lib/data/location/TokenLocation.cpp create mode 100644 src/lib/data/location/TokenLocation.h create mode 100644 src/lib/data/location/TokenLocationCollection.cpp create mode 100644 src/lib/data/location/TokenLocationCollection.h create mode 100644 src/lib/data/location/TokenLocationFile.cpp create mode 100644 src/lib/data/location/TokenLocationFile.h create mode 100644 src/lib/data/location/TokenLocationLine.cpp create mode 100644 src/lib/data/location/TokenLocationLine.h create mode 100644 src/test/TokenLocationCollectionTestSuite.h diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index bee70a1e..cd931d05 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -53,6 +53,15 @@ add_files( data/graph/Token.cpp data/graph/Token.h + data/location/TokenLocation.cpp + data/location/TokenLocation.h + data/location/TokenLocationCollection.cpp + data/location/TokenLocationCollection.h + data/location/TokenLocationFile.cpp + data/location/TokenLocationFile.h + data/location/TokenLocationLine.cpp + data/location/TokenLocationLine.h + data/parser/ParseLocation.cpp data/parser/ParseLocation.h data/parser/Parser.cpp @@ -68,12 +77,6 @@ add_files( data/SearchIndex.h data/Storage.cpp data/Storage.h - data/TextLocation.cpp - data/TextLocation.h - data/TextLocationFile.cpp - data/TextLocationFile.h - data/TextLocationLine.cpp - data/TextLocationLine.h gui/GuiArea.cpp gui/GuiArea.h diff --git a/src/lib/Project.cpp b/src/lib/Project.cpp index df11ae24..ab0020c2 100644 --- a/src/lib/Project.cpp +++ b/src/lib/Project.cpp @@ -46,6 +46,8 @@ void Project::parseCode() parser.parseFiles( FileSystem::getSourceFilesFromDirectory(ProjectSettings::getInstance()->getSourcePath(), extension) ); + m_storage->logGraph(); + m_storage->logLocations(); } } diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 81caba2e..7e3bdf3e 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -2,6 +2,7 @@ #include +#include "data/location/TokenLocation.h" #include "data/parser/ParseLocation.h" #include "data/parser/ParseVariable.h" #include "utility/logging/logging.h" @@ -29,6 +30,8 @@ void Storage::onClassParsed(const ParseLocation& location, const std::string& fu Node* node = m_graph.createNodeHierarchy(fullName); node->setType(Node::NODE_CLASS); node->setAccess(convertAccessType(access)); + + addTokenLocation(node, location); } void Storage::onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) @@ -38,6 +41,8 @@ void Storage::onStructParsed(const ParseLocation& location, const std::string& f Node* node = m_graph.createNodeHierarchy(fullName); node->setType(Node::NODE_STRUCT); node->setAccess(convertAccessType(access)); + + addTokenLocation(node, location); } void Storage::onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) @@ -50,6 +55,8 @@ void Storage::onGlobalVariableParsed(const ParseLocation& location, const ParseV node->setStatic(variable.isStatic); m_graph.createEdge(Edge::EDGE_TYPE_OF, node, m_graph.createNodeHierarchy(variable.typeName)); + + addTokenLocation(node, location); } void Storage::onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) @@ -69,6 +76,8 @@ void Storage::onFieldParsed(const ParseLocation& location, const ParseVariable& node->setAccess(convertAccessType(access)); m_graph.createEdge(Edge::EDGE_TYPE_OF, node, m_graph.createNodeHierarchy(variable.typeName)); + + addTokenLocation(node, location); } void Storage::onFunctionParsed( @@ -86,6 +95,8 @@ void Storage::onFunctionParsed( { m_graph.createEdge(Edge::EDGE_PARAMETER_OF, node, m_graph.createNodeHierarchy(var.typeName)); } + + addTokenLocation(node, location); } void Storage::onMethodParsed( @@ -113,6 +124,8 @@ void Storage::onMethodParsed( { m_graph.createEdge(Edge::EDGE_PARAMETER_OF, node, m_graph.createNodeHierarchy(parameter.typeName)); } + + addTokenLocation(node, location); } void Storage::onNamespaceParsed(const ParseLocation& location, const std::string& fullName) @@ -121,6 +134,8 @@ void Storage::onNamespaceParsed(const ParseLocation& location, const std::string Node* node = m_graph.createNodeHierarchy(fullName); node->setType(Node::NODE_NAMESPACE); + + addTokenLocation(node, location); } void Storage::onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) @@ -130,6 +145,8 @@ void Storage::onEnumParsed(const ParseLocation& location, const std::string& ful Node* node = m_graph.createNodeHierarchy(fullName); node->setType(Node::NODE_ENUM); node->setAccess(convertAccessType(access)); + + addTokenLocation(node, location); } void Storage::onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) @@ -138,6 +155,8 @@ void Storage::onEnumFieldParsed(const ParseLocation& location, const std::string Node* node = m_graph.createNodeHierarchy(fullName); node->setType(Node::NODE_FIELD); + + addTokenLocation(node, location); } void Storage::logGraph() const @@ -147,11 +166,11 @@ void Storage::logGraph() const LOG_INFO(str.str()); } -void Storage::log(std::string type, std::string str, const ParseLocation& location) const +void Storage::logLocations() const { - std::stringstream info; - info << type << ": " << str << " <" << location.file << " " << location.line << ":" << location.column << ">"; - LOG_INFO(info.str()); + std::stringstream str; + str << "\n" << m_locationCollection; + LOG_INFO(str.str()); } Edge::AccessType Storage::convertAccessType(ParserClient::AccessType access) const @@ -168,3 +187,25 @@ Edge::AccessType Storage::convertAccessType(ParserClient::AccessType access) con return Edge::ACCESS_NONE; } } + +TokenLocation* Storage::addTokenLocation(Token* token, const ParseLocation& loc) +{ + TokenLocation* location = m_locationCollection.addTokenLocation( + token->getId(), loc.filePath, + loc.startLineNumber, loc.startColumnNumber, + loc.endLineNumber, loc.endColumnNumber + ); + + token->addLocationId(location->getId()); + return location; +} + +void Storage::log(std::string type, std::string str, const ParseLocation& location) const +{ + std::stringstream info; + info << type << ": " << str; + info << " <" << location.filePath << " "; + info << location.startLineNumber << ":" << location.startColumnNumber << " "; + info << location.endLineNumber << ":" << location.endColumnNumber << ">"; + LOG_INFO(info.str()); +} diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index bdb6f748..d529c720 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -5,8 +5,8 @@ #include #include "data/graph/Graph.h" +#include "data/location/TokenLocationCollection.h" #include "data/parser/ParserClient.h" -#include "data/TextLocationFile.h" class Storage: public ParserClient { @@ -38,14 +38,16 @@ public: virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName); void logGraph() const; + void logLocations() const; private: - void log(std::string type, std::string str, const ParseLocation& location) const; Edge::AccessType convertAccessType(ParserClient::AccessType access) const; + TokenLocation* addTokenLocation(Token* token, const ParseLocation& location); + + void log(std::string type, std::string str, const ParseLocation& location) const; Graph m_graph; - - std::vector > m_textLocationFiles; + TokenLocationCollection m_locationCollection; }; #endif // STORAGE_H diff --git a/src/lib/data/TextLocation.cpp b/src/lib/data/TextLocation.cpp deleted file mode 100644 index 5c9597b2..00000000 --- a/src/lib/data/TextLocation.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "data/TextLocation.h" - -#include "data/graph/Token.h" -#include "data/TextLocationLine.h" - -TextLocation::TextLocation( - std::weak_ptr textLocationLine, - std::weak_ptr token, - unsigned int column -) - : m_token(token) - , m_textLocationLine(textLocationLine) - , m_column(column) -{ -} - -TextLocation::~TextLocation() -{ -} - -unsigned int TextLocation::getColumn() const -{ - return m_column; -} diff --git a/src/lib/data/TextLocation.h b/src/lib/data/TextLocation.h deleted file mode 100644 index 940fdc46..00000000 --- a/src/lib/data/TextLocation.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEXT_LOCATION_H -#define TEXT_LOCATION_H - -#include - -class Token; -class TextLocationLine; - -class TextLocation -{ -public: - TextLocation( - std::weak_ptr textLocationLine, - std::weak_ptr token, - unsigned int column - ); - - ~TextLocation(); - - unsigned int getColumn() const; - -private: - const std::weak_ptr m_token; - const std::weak_ptr m_textLocationLine; - const unsigned int m_column; -}; - -#endif // TEXT_LOCATION_H diff --git a/src/lib/data/TextLocationFile.cpp b/src/lib/data/TextLocationFile.cpp deleted file mode 100644 index 67dda7f9..00000000 --- a/src/lib/data/TextLocationFile.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "data/TextLocationFile.h" - -TextLocationFile::TextLocationFile(const std::string& filePath) -: m_filePath(filePath) -{ -} - -TextLocationFile::~TextLocationFile() -{ -} diff --git a/src/lib/data/TextLocationFile.h b/src/lib/data/TextLocationFile.h deleted file mode 100644 index 75696072..00000000 --- a/src/lib/data/TextLocationFile.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEXT_LOCATION_FILE_H -#define TEXT_LOCATION_FILE_H - -#include -#include -#include - -#include "data/TextLocationLine.h" - -class TextLocationFile -{ -public: - TextLocationFile(const std::string& filePath); - ~TextLocationFile(); - -private: - const std::string m_filePath; - std::vector > m_textLocationLine; -}; - - -#endif // TEXT_LOCATION_FILE_H diff --git a/src/lib/data/TextLocationLine.cpp b/src/lib/data/TextLocationLine.cpp deleted file mode 100644 index dea02584..00000000 --- a/src/lib/data/TextLocationLine.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "data/TextLocationLine.h" - -#include "data/TextLocationFile.h" - -TextLocationLine::TextLocationLine(std::weak_ptr textLocationFile, unsigned int lineNumber) - : m_textLocationFile(textLocationFile) - , m_lineNumber(lineNumber) -{ -} - -TextLocationLine::~TextLocationLine() -{ -} - -unsigned int TextLocationLine::getLineNumber() const -{ - return m_lineNumber; -} diff --git a/src/lib/data/TextLocationLine.h b/src/lib/data/TextLocationLine.h deleted file mode 100644 index f0cda0cc..00000000 --- a/src/lib/data/TextLocationLine.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEXT_LOCATION_LINE_H -#define TEXT_LOCATION_LINE_H - -#include -#include - -#include "data/TextLocation.h" - -class TextLocationFile; - -class TextLocationLine -{ -public: - TextLocationLine(std::weak_ptr textLocationFile, unsigned int lineNumber); - - ~TextLocationLine(); - - unsigned int getLineNumber() const; - -private: - const std::weak_ptr m_textLocationFile; - const unsigned int m_lineNumber; - - const std::vector > m_textLocations; -}; - - -#endif // TEXT_LOCATION_LINE_H diff --git a/src/lib/data/graph/Token.cpp b/src/lib/data/graph/Token.cpp index b56b851a..db0f48a3 100644 --- a/src/lib/data/graph/Token.cpp +++ b/src/lib/data/graph/Token.cpp @@ -1,5 +1,8 @@ #include "data/graph/Token.h" +#include "data/location/TokenLocation.h" +#include "utility/logging/logging.h" + Token::Token() : m_id(s_nextId++) { @@ -14,9 +17,33 @@ Id Token::getId() const return m_id; } -Id Token::s_nextId = 1; +const std::vector& Token::getLocationIds() const +{ + return m_locationIds; +} + +void Token::addLocationId(Id locationId) +{ + m_locationIds.push_back(locationId); +} + +void Token::removeLocationId(Id locationId) +{ + for (std::vector::const_iterator it = m_locationIds.begin(); it != m_locationIds.end(); it++) + { + if (*it == locationId) + { + m_locationIds.erase(it); + return; + } + } + + LOG_ERROR("Location Id was not referenced by this Token."); +} Token::Token(Id id) : m_id(id) { } + +Id Token::s_nextId = 1; diff --git a/src/lib/data/graph/Token.h b/src/lib/data/graph/Token.h index f2a2d3b8..7f8eefda 100644 --- a/src/lib/data/graph/Token.h +++ b/src/lib/data/graph/Token.h @@ -1,6 +1,8 @@ #ifndef TOKEN_H #define TOKEN_H +#include + #include "utility/types.h" class Token @@ -14,6 +16,11 @@ public: virtual bool isNode() const = 0; virtual bool isEdge() const = 0; + const std::vector& getLocationIds() const; + + void addLocationId(Id locationId); + void removeLocationId(Id locationId); + protected: // Constructor for plain copies of Node and Edge Token(Id id); @@ -25,6 +32,8 @@ private: void operator=(const Token&); const Id m_id; + + std::vector m_locationIds; }; #endif // TOKEN_H diff --git a/src/lib/data/location/TokenLocation.cpp b/src/lib/data/location/TokenLocation.cpp new file mode 100644 index 00000000..7629280f --- /dev/null +++ b/src/lib/data/location/TokenLocation.cpp @@ -0,0 +1,126 @@ +#include "data/location/TokenLocation.h" + +#include "data/location/TokenLocationLine.h" + +TokenLocation::TokenLocation(Id tokenId, TokenLocationLine* line, unsigned int columnNumber, bool isStart) + : TokenLocation(s_locationId++, tokenId, line, columnNumber, isStart) +{ +} + +TokenLocation::TokenLocation(Id id, Id tokenId, TokenLocationLine* line, unsigned int columnNumber, bool isStart) + : m_id(id) + , m_tokenId(tokenId) + , m_line(line) + , m_columnNumber(columnNumber) + , m_other(nullptr) + , m_isStart(isStart) +{ +} + +TokenLocation::~TokenLocation() +{ +} + +Id TokenLocation::getId() const +{ + return m_id; +} + +Id TokenLocation::getTokenId() const +{ + return m_tokenId; +} + +TokenLocationLine* TokenLocation::getTokenLocationLine() const +{ + return m_line; +} + +TokenLocationFile* TokenLocation::getTokenLocationFile() const +{ + return m_line->getTokenLocationFile(); +} + +unsigned int TokenLocation::getColumnNumber() const +{ + return m_columnNumber; +} + +unsigned int TokenLocation::getLineNumber() const +{ + return m_line->getLineNumber(); +} + +const std::string& TokenLocation::getFilePath() const +{ + return m_line->getFilePath(); +} + +TokenLocation* TokenLocation::getOtherTokenLocation() const +{ + return m_other; +} + +void TokenLocation::setOtherTokenLocation(TokenLocation* location) +{ + m_other = location; +} + +TokenLocation* TokenLocation::getStartTokenLocation() +{ + if (m_isStart) + { + return this; + } + else + { + return m_other; + } +} + +TokenLocation* TokenLocation::getEndTokenLocation() +{ + if (!m_isStart) + { + return this; + } + else + { + return m_other; + } +} + +bool TokenLocation::isStartTokenLocation() const +{ + return m_isStart; +} + +bool TokenLocation::isEndTokenLocation() const +{ + return !m_isStart; +} + +std::shared_ptr TokenLocation::createPlainCopy(TokenLocationLine* line) const +{ + return std::shared_ptr(new TokenLocation(m_id, m_tokenId, line, m_columnNumber, m_isStart)); +} + +Id TokenLocation::s_locationId = 1; + +std::ostream& operator<<(std::ostream& ostream, const TokenLocation& location) +{ + if ((&location)->isStartTokenLocation()) + { + ostream << "<"; + } + + ostream << location.getColumnNumber() << ":[" << location.getTokenId() << "]"; + + if ((&location)->isEndTokenLocation()) + { + ostream << ">"; + } + + ostream << " "; + return ostream; +} diff --git a/src/lib/data/location/TokenLocation.h b/src/lib/data/location/TokenLocation.h new file mode 100644 index 00000000..f4f6ada1 --- /dev/null +++ b/src/lib/data/location/TokenLocation.h @@ -0,0 +1,57 @@ +#ifndef TOKEN_LOCATION_H +#define TOKEN_LOCATION_H + +#include +#include +#include + +#include "utility/types.h" + +class Token; +class TokenLocationFile; +class TokenLocationLine; + +class TokenLocation +{ +public: + TokenLocation(Id tokenId, TokenLocationLine* line, unsigned int columnNumber, bool isStart); + TokenLocation(Id id, Id tokenId, TokenLocationLine* line, unsigned int columnNumber, bool isStart); + ~TokenLocation(); + + Id getId() const; + Id getTokenId() const; + + TokenLocationLine* getTokenLocationLine() const; + TokenLocationFile* getTokenLocationFile() const; + + unsigned int getColumnNumber() const; + unsigned int getLineNumber() const; + const std::string& getFilePath() const; + + TokenLocation* getOtherTokenLocation() const; + void setOtherTokenLocation(TokenLocation* location); + + TokenLocation* getStartTokenLocation(); + TokenLocation* getEndTokenLocation(); + + bool isStartTokenLocation() const; + bool isEndTokenLocation() const; + + std::shared_ptr createPlainCopy(TokenLocationLine* line) const; + +private: + static Id s_locationId; + + const Id m_id; + const Id m_tokenId; + + TokenLocationLine* const m_line; + const unsigned int m_columnNumber; + + TokenLocation* m_other; + const bool m_isStart; +}; + +std::ostream& operator<<(std::ostream& ostream, const TokenLocation& location); + +#endif // TOKEN_LOCATION_H diff --git a/src/lib/data/location/TokenLocationCollection.cpp b/src/lib/data/location/TokenLocationCollection.cpp new file mode 100644 index 00000000..fea44849 --- /dev/null +++ b/src/lib/data/location/TokenLocationCollection.cpp @@ -0,0 +1,138 @@ +#include "data/location/TokenLocationCollection.h" + +#include "data/location/TokenLocation.h" +#include "data/location/TokenLocationFile.h" +#include "data/location/TokenLocationLine.h" +#include "utility/logging/logging.h" + +TokenLocationCollection::TokenLocationCollection() +{ +} + +TokenLocationCollection::~TokenLocationCollection() +{ +} + +const TokenLocationCollection::TokenLocationFileMapType& TokenLocationCollection::getTokenLocationFiles() const +{ + return m_files; +} + +size_t TokenLocationCollection::getTokenLocationFileCount() const +{ + return m_files.size(); +} + +const std::map& TokenLocationCollection::getTokenLocations() const +{ + return m_locations; +} + +size_t TokenLocationCollection::getTokenLocationCount() const +{ + return m_locations.size(); +} + +TokenLocation* TokenLocationCollection::addTokenLocation( + Id tokenId, const std::string& filePath, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber) +{ + if (startLineNumber > endLineNumber || (startLineNumber == endLineNumber && startColumnNumber > endColumnNumber)) + { + LOG_ERROR("Can't create TokenLocation with wrong boundaries."); + return nullptr; + } + + TokenLocationFile* file = createTokenLocationFile(filePath); + TokenLocation* location = + file->addTokenLocation(tokenId, startLineNumber, startColumnNumber, endLineNumber, endColumnNumber); + + m_locations.emplace(location->getId(), location); + return location; +} + +void TokenLocationCollection::removeTokenLocation(TokenLocation* location) +{ + if (!findTokenLocationById(location->getId())) + { + LOG_ERROR("TokenLocation is not part of this TokenLocationCollection."); + return; + } + + m_locations.erase(location->getId()); + + TokenLocationFile* file = location->getTokenLocationFile(); + file->removeTokenLocation(location); + + if (!file->getTokenLocationLineCount()) + { + m_files.erase(file->getFilePath()); + } +} + +TokenLocation* TokenLocationCollection::findTokenLocationById(Id id) const +{ + std::map::const_iterator it = m_locations.find(id); + + if (it != m_locations.end()) + { + return it->second; + } + + return nullptr; +} + +TokenLocationFile* TokenLocationCollection::findTokenLocationFileByPath(const std::string& filePath) const +{ + std::map >::const_iterator it = m_files.find(filePath); + + if (it != m_files.end()) + { + return it->second.get(); + } + + return nullptr; +} + +void TokenLocationCollection::forEachTokenLocationFile(std::function func) const +{ + for (const TokenLocationFilePairType& file : m_files) + { + func(file.second.get()); + } +} + +TokenLocation* TokenLocationCollection::addTokenLocationAsPlainCopy(const TokenLocation* location) +{ + const std::string& filePath = location->getTokenLocationLine()->getTokenLocationFile()->getFilePath(); + TokenLocationFile* file = createTokenLocationFile(filePath); + TokenLocation* copy = file->addTokenLocationAsPlainCopy(location); + + m_locations.emplace(copy->getId(), copy); + return copy; +} + +TokenLocationFile* TokenLocationCollection::createTokenLocationFile(const std::string& filePath) +{ + TokenLocationFile* file = findTokenLocationFileByPath(filePath); + + if (file) + { + return file; + } + + std::shared_ptr filePtr = std::make_shared(filePath); + m_files.emplace(filePath, filePtr); + return filePtr.get(); +} + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationCollection& base) +{ + ostream << "Locations:\n"; + base.forEachTokenLocationFile([&ostream](TokenLocationFile* f) + { + ostream << *f; + }); + return ostream; +} diff --git a/src/lib/data/location/TokenLocationCollection.h b/src/lib/data/location/TokenLocationCollection.h new file mode 100644 index 00000000..0b586592 --- /dev/null +++ b/src/lib/data/location/TokenLocationCollection.h @@ -0,0 +1,51 @@ +#ifndef TOKEN_LOCATION_COLLECTION_H +#define TOKEN_LOCATION_COLLECTION_H + +#include +#include +#include +#include + +#include "utility/types.h" + +class TokenLocation; +class TokenLocationFile; + +class TokenLocationCollection +{ +public: + typedef std::map > TokenLocationFileMapType; + typedef std::pair > TokenLocationFilePairType; + + TokenLocationCollection(); + ~TokenLocationCollection(); + + const TokenLocationFileMapType& getTokenLocationFiles() const; + size_t getTokenLocationFileCount() const; + + const std::map& getTokenLocations() const; + size_t getTokenLocationCount() const; + + TokenLocation* addTokenLocation( + Id tokenId, const std::string& filePath, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber); + void removeTokenLocation(TokenLocation* location); + + TokenLocation* findTokenLocationById(Id id) const; + TokenLocationFile* findTokenLocationFileByPath(const std::string& filePath) const; + + void forEachTokenLocationFile(std::function func) const; + + TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); + +private: + TokenLocationFile* createTokenLocationFile(const std::string& filePath); + + TokenLocationFileMapType m_files; + std::map m_locations; +}; + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationCollection& base); + +#endif // TOKEN_LOCATION_COLLECTION_H diff --git a/src/lib/data/location/TokenLocationFile.cpp b/src/lib/data/location/TokenLocationFile.cpp new file mode 100644 index 00000000..e8d98bc6 --- /dev/null +++ b/src/lib/data/location/TokenLocationFile.cpp @@ -0,0 +1,152 @@ +#include "data/location/TokenLocationFile.h" + +#include "data/location/TokenLocation.h" +#include "data/location/TokenLocationLine.h" +#include "utility/logging/logging.h" + +TokenLocationFile::TokenLocationFile(const std::string& filePath) + : m_filePath(filePath) +{ +} + +TokenLocationFile::~TokenLocationFile() +{ +} + +const TokenLocationFile::TokenLocationLineMapType& TokenLocationFile::getTokenLocationLines() const +{ + return m_lines; +} + +size_t TokenLocationFile::getTokenLocationLineCount() const +{ + return m_lines.size(); +} + +const std::string& TokenLocationFile::getFilePath() const +{ + return m_filePath; +} + +TokenLocation* TokenLocationFile::addTokenLocation( + Id tokenId, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber) +{ + TokenLocationLine* line = createTokenLocationLine(startLineNumber); + TokenLocation* start = line->addStartTokenLocation(tokenId, startColumnNumber); + + if (startLineNumber != endLineNumber) + { + line = createTokenLocationLine(endLineNumber); + } + + line->addEndTokenLocation(start, endColumnNumber); + + return start; +} + +void TokenLocationFile::removeTokenLocation(TokenLocation* location) +{ + TokenLocationLine* line = location->getTokenLocationLine(); + + TokenLocation* otherLocation = location->getOtherTokenLocation(); + TokenLocationLine* otherLine = otherLocation->getTokenLocationLine(); + + line->removeTokenLocation(location); + if (!line->getTokenLocationCount()) + { + m_lines.erase(line->getLineNumber()); + } + + otherLine->removeTokenLocation(otherLocation); + if (!otherLine->getTokenLocationCount()) + { + m_lines.erase(otherLine->getLineNumber()); + } +} + +void TokenLocationFile::forEachTokenLocationLine(std::function func) const +{ + for (const TokenLocationLinePairType& line : m_lines) + { + func(line.second.get()); + } +} + +TokenLocation* TokenLocationFile::addTokenLocationAsPlainCopy(const TokenLocation* location) +{ + unsigned int lineNumber = location->getTokenLocationLine()->getLineNumber(); + TokenLocationLine* line = createTokenLocationLine(lineNumber); + + // Check whether this location was already added or if the other TokenLocation was added. + TokenLocation* otherLocation = line->getTokenLocationById(location->getId()); + if (otherLocation) + { + if (otherLocation->isStartTokenLocation() == location->isStartTokenLocation()) + { + // The location was already added. + return otherLocation; + } + } + else + { + // Look for the other location in it's line. + unsigned int otherLineNumber = location->getOtherTokenLocation()->getTokenLocationLine()->getLineNumber(); + if (lineNumber != otherLineNumber) + { + TokenLocationLine* otherLine = findTokenLocationLine(otherLineNumber); + if (otherLine) + { + otherLocation = otherLine->getTokenLocationById(location->getId()); + } + } + } + + TokenLocation* copy = line->addTokenLocationAsPlainCopy(location); + + // If the other location was added before, then link them with each other. + if (otherLocation) + { + otherLocation->setOtherTokenLocation(copy); + copy->setOtherTokenLocation(otherLocation); + } + + return copy; +} + +TokenLocationLine* TokenLocationFile::findTokenLocationLine(unsigned int lineNumber) const +{ + TokenLocationLineMapType::const_iterator it = m_lines.find(lineNumber); + + if (it != m_lines.end()) + { + return it->second.get(); + } + + return nullptr; +} + +TokenLocationLine* TokenLocationFile::createTokenLocationLine(unsigned int lineNumber) +{ + TokenLocationLine* line = findTokenLocationLine(lineNumber); + + if (line) + { + return line; + } + + std::shared_ptr linePtr = std::make_shared(this, lineNumber); + m_lines.emplace(lineNumber, linePtr); + return linePtr.get(); +} + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationFile& file) +{ + ostream << "file \"" << file.getFilePath() << "\"\n"; + file.forEachTokenLocationLine([&ostream](TokenLocationLine* l) + { + ostream << *l << '\n'; + }); + return ostream; +} diff --git a/src/lib/data/location/TokenLocationFile.h b/src/lib/data/location/TokenLocationFile.h new file mode 100644 index 00000000..13f0ffa6 --- /dev/null +++ b/src/lib/data/location/TokenLocationFile.h @@ -0,0 +1,48 @@ +#ifndef TOKEN_LOCATION_FILE_H +#define TOKEN_LOCATION_FILE_H + +#include +#include +#include +#include + +#include "utility/types.h" + +class TokenLocation; +class TokenLocationLine; + +class TokenLocationFile +{ +public: + typedef std::map > TokenLocationLineMapType; + typedef std::pair > TokenLocationLinePairType; + + TokenLocationFile(const std::string& filePath); + ~TokenLocationFile(); + + const TokenLocationLineMapType& getTokenLocationLines() const; + size_t getTokenLocationLineCount() const; + + const std::string& getFilePath() const; + + TokenLocation* addTokenLocation( + Id tokenId, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber); + void removeTokenLocation(TokenLocation* location); + + void forEachTokenLocationLine(std::function func) const; + + TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); + +private: + TokenLocationLine* findTokenLocationLine(unsigned int lineNumber) const; + TokenLocationLine* createTokenLocationLine(unsigned int lineNumber); + + std::map > m_lines; + const std::string m_filePath; +}; + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationFile& file); + +#endif // TOKEN_LOCATION_FILE_H diff --git a/src/lib/data/location/TokenLocationLine.cpp b/src/lib/data/location/TokenLocationLine.cpp new file mode 100644 index 00000000..81ba5c50 --- /dev/null +++ b/src/lib/data/location/TokenLocationLine.cpp @@ -0,0 +1,113 @@ +#include "data/location/TokenLocationLine.h" + +#include "data/location/TokenLocation.h" +#include "data/location/TokenLocationFile.h" +#include "utility/logging/logging.h" + +TokenLocationLine::TokenLocationLine(TokenLocationFile* file, unsigned int lineNumber) + : m_file(file) + , m_lineNumber(lineNumber) +{ +} + +TokenLocationLine::~TokenLocationLine() +{ +} + +const TokenLocationLine::TokenLocationMapType& TokenLocationLine::getTokenLocations() const +{ + return m_locations; +} + +size_t TokenLocationLine::getTokenLocationCount() const +{ + return m_locations.size(); +} + +TokenLocationFile* TokenLocationLine::getTokenLocationFile() const +{ + return m_file; +} + +const std::string& TokenLocationLine::getFilePath() const +{ + return m_file->getFilePath(); +} + +unsigned int TokenLocationLine::getLineNumber() const +{ + return m_lineNumber; +} + +TokenLocation* TokenLocationLine::addStartTokenLocation(Id tokenId, unsigned int columnNumber) +{ + std::shared_ptr locationPtr = std::make_shared(tokenId, this, columnNumber, true); + m_locations.emplace(columnNumber, locationPtr); + return locationPtr.get(); +} + +TokenLocation* TokenLocationLine::addEndTokenLocation(TokenLocation* start, unsigned int columnNumber) +{ + std::shared_ptr locationPtr = std::make_shared( + start->getId(), start->getTokenId(), this, columnNumber, false); + + start->setOtherTokenLocation(locationPtr.get()); + locationPtr->setOtherTokenLocation(start); + + m_locations.emplace(columnNumber, locationPtr); + return locationPtr.get(); +} + +void TokenLocationLine::removeTokenLocation(TokenLocation* location) +{ + TokenLocationMapType::iterator it = m_locations.find(location->getColumnNumber()); + + while (it->first == location->getColumnNumber()) + { + if (it->second.get() == location) + { + m_locations.erase(it); + return; + } + it++; + } + + LOG_ERROR("TokenLocation can't be removed, it's not part of the TokenLocationLine."); +} + +TokenLocation* TokenLocationLine::getTokenLocationById(Id id) const +{ + for (const TokenLocationPairType& p : m_locations) + { + if (p.second->getId() == id) + { + return p.second.get(); + } + } + return nullptr; +} + +void TokenLocationLine::forEachTokenLocation(std::function func) const +{ + for (const TokenLocationPairType& location : m_locations) + { + func(location.second.get()); + } +} + +TokenLocation* TokenLocationLine::addTokenLocationAsPlainCopy(const TokenLocation* location) +{ + std::shared_ptr locationPtr = location->createPlainCopy(this); + m_locations.emplace(location->getColumnNumber(), locationPtr); + return locationPtr.get(); +} + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationLine& line) +{ + ostream << line.getLineNumber() << ": "; + line.forEachTokenLocation([&ostream](TokenLocation* l) + { + ostream << *l; + }); + return ostream; +} diff --git a/src/lib/data/location/TokenLocationLine.h b/src/lib/data/location/TokenLocationLine.h new file mode 100644 index 00000000..2b6a61f7 --- /dev/null +++ b/src/lib/data/location/TokenLocationLine.h @@ -0,0 +1,50 @@ +#ifndef TOKEN_LOCATION_LINE_H +#define TOKEN_LOCATION_LINE_H + +#include +#include +#include +#include + +#include "utility/types.h" + +class TokenLocation; +class TokenLocationFile; + +class TokenLocationLine +{ +public: + typedef std::multimap > TokenLocationMapType; + typedef std::pair > TokenLocationPairType; + + TokenLocationLine(TokenLocationFile* file, unsigned int lineNumber); + ~TokenLocationLine(); + + const TokenLocationMapType& getTokenLocations() const; + size_t getTokenLocationCount() const; + + TokenLocationFile* getTokenLocationFile() const; + const std::string& getFilePath() const; + + unsigned int getLineNumber() const; + + TokenLocation* addStartTokenLocation(Id tokenId, unsigned int columnNumber); + TokenLocation* addEndTokenLocation(TokenLocation* start, unsigned int columnNumber); + void removeTokenLocation(TokenLocation* location); + + TokenLocation* getTokenLocationById(Id id) const; + + void forEachTokenLocation(std::function func) const; + + TokenLocation* addTokenLocationAsPlainCopy(const TokenLocation* location); + +private: + TokenLocationMapType m_locations; + + TokenLocationFile* const m_file; + const unsigned int m_lineNumber; +}; + +std::ostream& operator<<(std::ostream& ostream, const TokenLocationLine& line); + +#endif // TOKEN_LOCATION_LINE_H diff --git a/src/lib/data/parser/ParseLocation.cpp b/src/lib/data/parser/ParseLocation.cpp index 45bf967f..e55cf65a 100644 --- a/src/lib/data/parser/ParseLocation.cpp +++ b/src/lib/data/parser/ParseLocation.cpp @@ -1,8 +1,14 @@ #include "data/parser/ParseLocation.h" -ParseLocation::ParseLocation(const std::string& file, unsigned int line, unsigned int column) - : file(file) - , line(line) - , column(column) +ParseLocation::ParseLocation( + const std::string& filePath, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber +) + : filePath(filePath) + , startLineNumber(startLineNumber) + , startColumnNumber(startColumnNumber) + , endLineNumber(endLineNumber) + , endColumnNumber(endColumnNumber) { } diff --git a/src/lib/data/parser/ParseLocation.h b/src/lib/data/parser/ParseLocation.h index 47999120..7c58832e 100644 --- a/src/lib/data/parser/ParseLocation.h +++ b/src/lib/data/parser/ParseLocation.h @@ -5,11 +5,16 @@ struct ParseLocation { - ParseLocation(const std::string& file, unsigned int line, unsigned int column); + ParseLocation( + const std::string& filePath, + unsigned int startLineNumber, unsigned int startColumnNumber, + unsigned int endLineNumber, unsigned int endColumnNumber); - const std::string file; - const unsigned int line; - const unsigned int column; + const std::string filePath; + unsigned int startLineNumber; + unsigned int startColumnNumber; + unsigned int endLineNumber; + unsigned int endColumnNumber; }; #endif // PARSE_LOCATION_H diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp index 2f15c302..3beddcd4 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.cpp +++ b/src/lib/data/parser/cxx/ASTVisitor.cpp @@ -15,12 +15,10 @@ ASTVisitor::~ASTVisitor() bool ASTVisitor::VisitTypedefDecl(const clang::TypedefDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { m_client->onTypedefParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), declaration->getUnderlyingType().getAsString(), convertAccessType(declaration->getAccess()) @@ -32,14 +30,12 @@ bool ASTVisitor::VisitTypedefDecl(const clang::TypedefDecl* declaration) bool ASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { if (declaration->isClass()) { m_client->onClassParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), convertAccessType(declaration->getAccess()) ); @@ -47,7 +43,7 @@ bool ASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* declaration) else if (declaration->isStruct()) { m_client->onStructParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), convertAccessType(declaration->getAccess()) ); @@ -65,20 +61,18 @@ bool ASTVisitor::VisitVarDecl(clang::VarDecl* declaration) return true; } - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { clang::AccessSpecifier access = declaration->getAccess(); if (access == clang::AS_none) { - m_client->onGlobalVariableParsed(getParseLocation(location), getParseVariable(declaration)); + m_client->onGlobalVariableParsed(getParseLocation(declaration), getParseVariable(declaration)); } else { m_client->onFieldParsed( - getParseLocation(location), + getParseLocation(declaration), getParseVariable(declaration), convertAccessType(declaration->getAccess()) ); @@ -90,12 +84,10 @@ bool ASTVisitor::VisitVarDecl(clang::VarDecl* declaration) bool ASTVisitor::VisitFieldDecl(clang::FieldDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { m_client->onFieldParsed( - getParseLocation(location), + getParseLocation(declaration), getParseVariable(declaration), convertAccessType(declaration->getAccess()) ); @@ -112,12 +104,10 @@ bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration) return true; } - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { m_client->onFunctionParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), getTypeName(declaration->getReturnType()), getParameters(declaration) @@ -129,9 +119,7 @@ bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration) bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { ParserClient::AbstractionType abstraction = ParserClient::ABSTRACTION_NONE; if (declaration->isPure()) @@ -144,7 +132,7 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) } m_client->onMethodParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), getTypeName(declaration->getReturnType()), getParameters(declaration), @@ -160,11 +148,9 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) bool ASTVisitor::VisitNamespaceDecl(clang::NamespaceDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { - m_client->onNamespaceParsed(getParseLocation(location), declaration->getQualifiedNameAsString()); + m_client->onNamespaceParsed(getParseLocation(declaration), declaration->getQualifiedNameAsString()); } return true; @@ -172,12 +158,10 @@ bool ASTVisitor::VisitNamespaceDecl(clang::NamespaceDecl* declaration) bool ASTVisitor::VisitEnumDecl(clang::EnumDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { m_client->onEnumParsed( - getParseLocation(location), + getParseLocation(declaration), declaration->getQualifiedNameAsString(), convertAccessType(declaration->getAccess()) ); @@ -188,28 +172,34 @@ bool ASTVisitor::VisitEnumDecl(clang::EnumDecl* declaration) bool ASTVisitor::VisitEnumConstantDecl(clang::EnumConstantDecl* declaration) { - const clang::SourceLocation& location = declaration->getLocStart(); - - if (isValidLocation(location)) + if (hasValidLocation(declaration)) { - m_client->onEnumFieldParsed(getParseLocation(location), declaration->getQualifiedNameAsString()); + m_client->onEnumFieldParsed(getParseLocation(declaration), declaration->getQualifiedNameAsString()); } return true; } -bool ASTVisitor::isValidLocation(const clang::SourceLocation& location) const +bool ASTVisitor::hasValidLocation(const clang::Decl* declaration) const { + const clang::SourceLocation& location = declaration->getLocStart(); return location.isValid() && m_context->getSourceManager().isWrittenInMainFile(location); } -ParseLocation ASTVisitor::getParseLocation(const clang::SourceLocation& location) const +ParseLocation ASTVisitor::getParseLocation(const clang::Decl* declaration) const { - clang::FullSourceLoc fullLocation = m_context->getFullLoc(location); + const clang::SourceRange& sourceRange = declaration->getSourceRange(); + const clang::SourceManager& sourceManager = m_context->getSourceManager(); + + const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(sourceRange.getBegin()); + const clang::PresumedLoc& presumedEnd = sourceManager.getPresumedLoc(sourceRange.getEnd()); + return ParseLocation( - m_context->getSourceManager().getFilename(location), - fullLocation.getSpellingLineNumber(), - fullLocation.getSpellingColumnNumber() + presumedBegin.getFilename(), + presumedBegin.getLine(), + presumedBegin.getColumn(), + presumedEnd.getLine(), + presumedEnd.getColumn() ); } diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h index df234148..05bb77cc 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.h +++ b/src/lib/data/parser/cxx/ASTVisitor.h @@ -33,8 +33,8 @@ public: virtual bool VisitEnumConstantDecl(clang::EnumConstantDecl* declaration); // enum fields private: - bool isValidLocation(const clang::SourceLocation& location) const; - ParseLocation getParseLocation(const clang::SourceLocation& location) const; + bool hasValidLocation(const clang::Decl* declaration) const; + ParseLocation getParseLocation(const clang::Decl* declaration) const; ParseVariable getParseVariable(clang::ValueDecl* declaration) const; std::vector getParameters(clang::FunctionDecl* declaration) const; std::string getTypeName(const clang::QualType& type) const; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a7ecc21b..90717843 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -11,6 +11,7 @@ add_files( TestSuiteFixture.cpp TestSuiteFixture.h TextAccessTestSuite.h + TokenLocationCollectionTestSuite.h UtilityStringTestSuite.h Vector2TestSuite.h ) diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index 56cf66eb..271de2ea 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -1,5 +1,7 @@ #include "cxxtest/TestSuite.h" +#include + #include "data/parser/cxx/CxxParser.h" #include "data/parser/ParseLocation.h" #include "data/parser/ParserClient.h" @@ -21,7 +23,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->classes.size(), 1); - TS_ASSERT_EQUALS(client->classes[0], "A"); + TS_ASSERT_EQUALS(client->classes[0], "A <1:1 3:1>"); } void test_cxx_parser_finds_global_class_forward_declaration() @@ -34,7 +36,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->classes.size(), 1); - TS_ASSERT_EQUALS(client->classes[0], "A"); + TS_ASSERT_EQUALS(client->classes[0], "A <1:1 1:7>"); } void test_cxx_parser_finds_nested_class_definition() // TODO: test different access types here @@ -51,8 +53,8 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->classes.size(), 2); - TS_ASSERT_EQUALS(client->classes[0], "A"); - TS_ASSERT_EQUALS(client->classes[1], "public A::B"); + TS_ASSERT_EQUALS(client->classes[0], "A <1:1 5:1>"); + TS_ASSERT_EQUALS(client->classes[1], "public A::B <4:2 4:8>"); } void test_cxx_parser_finds_class_definition_in_namespace() @@ -68,7 +70,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->classes.size(), 1); - TS_ASSERT_EQUALS(client->classes[0], "a::B"); + TS_ASSERT_EQUALS(client->classes[0], "a::B <3:2 3:8>"); } void test_cxx_parser_finds_global_struct_definition() @@ -83,7 +85,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->structs.size(), 1); - TS_ASSERT_EQUALS(client->structs[0], "A"); + TS_ASSERT_EQUALS(client->structs[0], "A <1:1 3:1>"); } void test_cxx_parser_finds_global_struct_forward_declaration() @@ -96,7 +98,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->structs.size(), 1); - TS_ASSERT_EQUALS(client->structs[0], "A"); + TS_ASSERT_EQUALS(client->structs[0], "A <1:1 1:8>"); } void test_cxx_parser_finds_struct_definition_in_class() @@ -114,7 +116,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->structs.size(), 1); - TS_ASSERT_EQUALS(client->structs[0], "private A::B"); + TS_ASSERT_EQUALS(client->structs[0], "private A::B <3:2 5:2>"); } void test_cxx_parser_finds_struct_definition_in_namespace() @@ -132,7 +134,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->structs.size(), 1); - TS_ASSERT_EQUALS(client->structs[0], "A::B"); + TS_ASSERT_EQUALS(client->structs[0], "A::B <3:2 5:2>"); } void test_cxx_parser_finds_variable_definitions_in_global_scope() @@ -147,8 +149,8 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->globals.size(), 2); - TS_ASSERT_EQUALS(client->globals[0], "int x"); - TS_ASSERT_EQUALS(client->globals[1], "A * b"); + TS_ASSERT_EQUALS(client->globals[0], "int x <1:1 1:5>"); + TS_ASSERT_EQUALS(client->globals[1], "A * b <3:1 3:4>"); } void test_cxx_parser_finds_variable_definitions_in_namespace_scope() @@ -166,8 +168,8 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->globals.size(), 2); - TS_ASSERT_EQUALS(client->globals[0], "int n::x"); - TS_ASSERT_EQUALS(client->globals[1], "n::A * n::b"); + TS_ASSERT_EQUALS(client->globals[0], "int n::x <2:2 2:6>"); + TS_ASSERT_EQUALS(client->globals[1], "n::A * n::b <4:2 4:5>"); } void test_cxx_parser_finds_field_in_nested_class() @@ -188,7 +190,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->fields.size(), 1); - TS_ASSERT_EQUALS(client->fields[0], "private static const int B::C::amount"); + TS_ASSERT_EQUALS(client->fields[0], "private static const int B::C::amount <7:3 7:20>"); } void test_cxx_parser_finds_field_in_global_class() @@ -199,15 +201,15 @@ public: "class B\n" "{\n" "public:\n" - " B() : count(0) {};" + " B() : count(0) {};\n" "private:\n" - " const int count;" + " const int count;\n" "};\n"; parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->fields.size(), 1); - TS_ASSERT_EQUALS(client->fields[0], "private const int B::count"); + TS_ASSERT_EQUALS(client->fields[0], "private const int B::count <6:2 6:12>"); } void test_cxx_parser_finds_function_in_global_namespace() @@ -223,7 +225,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->functions.size(), 1); - TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a)"); + TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a) <1:1 4:1>"); } void test_cxx_parser_finds_function_in_anonymous_namespace() @@ -239,7 +241,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->functions.size(), 1); - TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int a, int b)"); + TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int a, int b) <3:2 3:22>"); } void test_cxx_parser_finds_method_declaration() @@ -256,7 +258,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->methods.size(), 1); - TS_ASSERT_EQUALS(client->methods[0], "public void B::B()"); + TS_ASSERT_EQUALS(client->methods[0], "public void B::B() <4:2 4:4>"); } void test_cxx_parser_finds_method_declaration_and_definition() @@ -276,8 +278,8 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->methods.size(), 2); - TS_ASSERT_EQUALS(client->methods[0], "public void B::B()"); - TS_ASSERT_EQUALS(client->methods[1], "public void B::B()"); + TS_ASSERT_EQUALS(client->methods[0], "public void B::B() <4:2 4:4>"); + TS_ASSERT_EQUALS(client->methods[1], "public void B::B() <6:1 8:1>"); } void test_cxx_parser_finds_pure_virtual_method() @@ -294,7 +296,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->methods.size(), 1); - TS_ASSERT_EQUALS(client->methods[0], "protected pure virtual void B::process()"); + TS_ASSERT_EQUALS(client->methods[0], "protected pure virtual void B::process() <4:2 4:27>"); } void test_cxx_parser_finds_method_declared_in_nested_class() @@ -313,7 +315,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->methods.size(), 1); - TS_ASSERT_EQUALS(client->methods[0], "private _Bool B::C::isGreat() const"); + TS_ASSERT_EQUALS(client->methods[0], "private _Bool B::C::isGreat() const <5:3 5:18>"); } void test_cxx_parser_finds_named_namespace() @@ -322,13 +324,13 @@ public: CxxParser parser(client); std::string text = "namespace A\n" - "{" + "{\n" "}\n"; parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->namespaces.size(), 1); - TS_ASSERT_EQUALS(client->namespaces[0], "A"); + TS_ASSERT_EQUALS(client->namespaces[0], "A <1:1 3:1>"); } void test_cxx_parser_finds_anonymous_namespace() @@ -343,7 +345,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->namespaces.size(), 1); - TS_ASSERT_EQUALS(client->namespaces[0], "(anonymous)"); + TS_ASSERT_EQUALS(client->namespaces[0], "(anonymous) <1:1 3:1>"); } void test_cxx_parser_finds_enum_defined_in_global_namespace() @@ -358,7 +360,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->enums.size(), 1); - TS_ASSERT_EQUALS(client->enums[0], "E"); + TS_ASSERT_EQUALS(client->enums[0], "E <1:1 3:1>"); } void test_cxx_parser_finds_enum_defined_in_class() @@ -377,7 +379,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->enums.size(), 1); - TS_ASSERT_EQUALS(client->enums[0], "public B::Z"); + TS_ASSERT_EQUALS(client->enums[0], "public B::Z <4:2 6:2>"); } void test_cxx_parser_finds_enum_defined_in_namespace() @@ -395,7 +397,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->enums.size(), 1); - TS_ASSERT_EQUALS(client->enums[0], "n::Z"); + TS_ASSERT_EQUALS(client->enums[0], "n::Z <3:2 5:2>"); } void test_cxx_parser_finds_enum_field_in_global_enum() @@ -411,7 +413,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->enumFields.size(), 1); - TS_ASSERT_EQUALS(client->enumFields[0], "E::P"); + TS_ASSERT_EQUALS(client->enumFields[0], "E::P <3:2 3:2>"); } void test_cxx_parser_finds_typedef_in_global_namespace() @@ -423,7 +425,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->typedefs.size(), 1); - TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> uint"); + TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> uint <1:1 1:22>"); } void test_cxx_parser_finds_typedef_in_named_namespace() @@ -439,7 +441,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->typedefs.size(), 1); - TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> test::uint"); + TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> test::uint <3:2 3:23>"); } void test_cxx_parser_finds_typedef_in_anonymous_namespace() @@ -455,7 +457,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->typedefs.size(), 1); - TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> (anonymous namespace)::uint"); + TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> (anonymous namespace)::uint <3:2 3:23>"); } void test_cxx_parser_finds_typedef_that_uses_type_defined_in_named_namespace() @@ -472,7 +474,7 @@ public: parser.parseFile(TextAccess::createFromString(text)); TS_ASSERT_EQUALS(client->typedefs.size(), 1); - TS_ASSERT_EQUALS(client->typedefs[0], "test::TestStruct -> globalTestStruct"); + TS_ASSERT_EQUALS(client->typedefs[0], "test::TestStruct -> globalTestStruct <5:1 5:26>"); } void test_cxx_parser_parses_multiple_files() @@ -506,27 +508,28 @@ private: AccessType access ) { - typedefs.push_back(addAccessPrefix(underlyingFullName + " -> " + fullName, access)); + std::string str = addAccessPrefix(underlyingFullName + " -> " + fullName, access); + typedefs.push_back(addLocationSuffix(str, location)); } virtual void onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { - classes.push_back(addAccessPrefix(fullName, access)); + classes.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location)); } virtual void onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { - structs.push_back(addAccessPrefix(fullName, access)); + structs.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location)); } virtual void onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) { - globals.push_back(variableStr(variable)); + globals.push_back(addLocationSuffix(variableStr(variable), location)); } virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) { - fields.push_back(addAccessPrefix(variableStr(variable), access)); + fields.push_back(addLocationSuffix(addAccessPrefix(variableStr(variable), access), location)); } virtual void onFunctionParsed( @@ -534,7 +537,8 @@ private: const std::vector& parameters ) { - functions.push_back(returnTypeName + " " + fullName + parameterStr(parameters)); + std::string str = returnTypeName + " " + fullName + parameterStr(parameters); + functions.push_back(addLocationSuffix(str, location)); } virtual void onMethodParsed( @@ -546,22 +550,23 @@ private: std::string str = returnTypeName + " " + fullName + parameterStr(parameters); str = addStaticPrefix(addAbstractionPrefix(str, abstraction), isStatic); str = addConstPrefix(addAccessPrefix(str, access), isConst, false); + str = addLocationSuffix(str, location); methods.push_back(str); } virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName) { - namespaces.push_back(fullName); + namespaces.push_back(addLocationSuffix(fullName, location)); } virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { - enums.push_back(addAccessPrefix(fullName, access)); + enums.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location)); } virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) { - enumFields.push_back(fullName); + enumFields.push_back(addLocationSuffix(fullName, location)); } std::vector typedefs; @@ -641,5 +646,13 @@ private: } return str + ")"; } + + std::string addLocationSuffix(const std::string& str, const ParseLocation& location) + { + std::stringstream ss; + ss << str << " <" << location.startLineNumber << ":" << location.startColumnNumber << " "; + ss << location.endLineNumber << ":" << location.endColumnNumber << ">"; + return ss.str(); + } }; }; diff --git a/src/test/TokenLocationCollectionTestSuite.h b/src/test/TokenLocationCollectionTestSuite.h new file mode 100644 index 00000000..f682bea8 --- /dev/null +++ b/src/test/TokenLocationCollectionTestSuite.h @@ -0,0 +1,162 @@ +#include "cxxtest/TestSuite.h" + +#include "data/location/TokenLocation.h" +#include "data/location/TokenLocationCollection.h" +#include "data/location/TokenLocationFile.h" +#include "data/location/TokenLocationLine.h" + +class TokenLocationCollectionTestSuite : public CxxTest::TestSuite +{ +public: + void test_token_locations_get_created_with_other_end() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5); + + TS_ASSERT(a); + TS_ASSERT(a->isStartTokenLocation()); + TS_ASSERT(!a->isEndTokenLocation()); + + TokenLocation* b = a->getOtherTokenLocation(); + + TS_ASSERT(b); + TS_ASSERT(!b->isStartTokenLocation()); + TS_ASSERT(b->isEndTokenLocation()); + + TS_ASSERT_EQUALS(a, b->getOtherTokenLocation()); + TS_ASSERT_EQUALS(a, b->getStartTokenLocation()); + TS_ASSERT_EQUALS(a, a->getStartTokenLocation()); + TS_ASSERT_EQUALS(b, a->getEndTokenLocation()); + TS_ASSERT_EQUALS(b, b->getEndTokenLocation()); + } + + void test_token_locations_do_not_get_created_with_wrong_input() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 2, 1); + TokenLocation* b = collection.addTokenLocation(1, "file.c", 4, 1, 1, 10); + + TS_ASSERT(!a); + TS_ASSERT(!b); + } + + void test_token_locations_get_unique_id_but_both_ends_have_the_same() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 1, 1, 1, 1); + TokenLocation* b = collection.addTokenLocation(2, "file.c", 1, 1, 1, 1); + TokenLocation* c = collection.addTokenLocation(3, "file.c", 1, 1, 1, 1); + + TS_ASSERT_EQUALS(1, collection.getTokenLocationFileCount()); + TS_ASSERT_EQUALS(3, collection.getTokenLocationCount()); + + TS_ASSERT_DIFFERS(a->getId(), b->getId()); + TS_ASSERT_DIFFERS(b->getId(), c->getId()); + TS_ASSERT_DIFFERS(c->getId(), a->getId()); + + TS_ASSERT_EQUALS(a->getId(), a->getOtherTokenLocation()->getId()); + TS_ASSERT_EQUALS(b->getId(), b->getOtherTokenLocation()->getId()); + TS_ASSERT_EQUALS(c->getId(), c->getOtherTokenLocation()->getId()); + } + + void test_token_locations_have_right_file_path_line_column_and_token_id() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5); + + TS_ASSERT_EQUALS(1, a->getTokenId()); + TS_ASSERT_EQUALS(2, a->getLineNumber()); + TS_ASSERT_EQUALS(3, a->getColumnNumber()); + TS_ASSERT_EQUALS(4, a->getOtherTokenLocation()->getLineNumber()); + TS_ASSERT_EQUALS(5, a->getOtherTokenLocation()->getColumnNumber()); + TS_ASSERT_EQUALS("file.c", a->getFilePath()); + } + + void test_finding_token_locations_by_id() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5); + TokenLocation* b = collection.addTokenLocation(6, "file.c", 7, 8, 9, 10); + + TS_ASSERT_EQUALS(a, collection.findTokenLocationById(a->getId())); + TS_ASSERT_EQUALS(b, collection.findTokenLocationById(b->getId())); + } + + void test_removing_token_locations() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5); + TokenLocation* b = collection.addTokenLocation(1, "file.c", 3, 3, 4, 5); + TokenLocation* c = collection.addTokenLocation(1, "file.c", 1, 3, 5, 5); + TokenLocation* d = collection.addTokenLocation(1, "file2.c", 1, 3, 5, 5); + + TS_ASSERT_EQUALS(2, collection.getTokenLocationFileCount()); + TS_ASSERT_EQUALS(4, collection.getTokenLocationCount()); + TS_ASSERT_EQUALS(5, c->getTokenLocationFile()->getTokenLocationLineCount()); + + Id ida = a->getId(); + Id idb = b->getId(); + Id idc = c->getId(); + + collection.removeTokenLocation(a); + collection.removeTokenLocation(b->getOtherTokenLocation()); + collection.removeTokenLocation(d); + + TS_ASSERT(!collection.findTokenLocationById(ida)); + TS_ASSERT(!collection.findTokenLocationById(idb)); + TS_ASSERT_EQUALS(c, collection.findTokenLocationById(idc)); + + TS_ASSERT_EQUALS(1, collection.getTokenLocationFileCount()); + TS_ASSERT_EQUALS(1, collection.getTokenLocationCount()); + TS_ASSERT_EQUALS(2, c->getTokenLocationFile()->getTokenLocationLineCount()); + } + + void test_creating_plain_copy_of_all_locations_in_line_range() + { + TokenLocationCollection collection; + TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5); + TokenLocation* b = collection.addTokenLocation(1, "file.c", 3, 3, 4, 5); + TokenLocation* c = collection.addTokenLocation(1, "file.c", 1, 3, 5, 5); + TokenLocation* d = collection.addTokenLocation(1, "file.c", 1, 5, 4, 5); + + Id ida = a->getId(); + Id idb = b->getId(); + Id idc = c->getId(); + Id idd = d->getId(); + + unsigned int fromLine = 2; + unsigned int toLine = 4; + + TokenLocationCollection copy; + TokenLocation* x = collection.findTokenLocationById(ida); + + x->getTokenLocationFile()->forEachTokenLocationLine([©, fromLine, toLine](TokenLocationLine* line) + { + unsigned int l = line->getLineNumber(); + if (l >= fromLine && l <= toLine) + { + line->forEachTokenLocation([©](TokenLocation* location) + { + copy.addTokenLocationAsPlainCopy(location); + }); + } + }); + + TS_ASSERT_EQUALS(1, copy.getTokenLocationFileCount()); + TS_ASSERT_EQUALS(3, copy.getTokenLocationCount()); + + TS_ASSERT(copy.findTokenLocationById(ida)); + TS_ASSERT(copy.findTokenLocationById(idb)); + TS_ASSERT(!copy.findTokenLocationById(idc)); + TS_ASSERT(copy.findTokenLocationById(idd)); + + TS_ASSERT_DIFFERS(a, copy.findTokenLocationById(ida)); + TS_ASSERT_DIFFERS(d, copy.findTokenLocationById(idd)); + + TS_ASSERT(copy.findTokenLocationById(ida)->getStartTokenLocation()); + TS_ASSERT(copy.findTokenLocationById(ida)->getEndTokenLocation()); + + TS_ASSERT(!copy.findTokenLocationById(idd)->getStartTokenLocation()); + TS_ASSERT(copy.findTokenLocationById(idd)->getEndTokenLocation()); + } +};