diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 627a52af..7c7ced5d 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -69,7 +69,7 @@ add_files( data/graph/edgeComponent/EdgeComponent.h data/graph/edgeComponent/EdgeComponentDataType.cpp data/graph/edgeComponent/EdgeComponentDataType.h - + data/graph/Edge.cpp data/graph/Edge.h data/graph/Graph.cpp @@ -94,6 +94,8 @@ add_files( data/parser/Parser.h data/parser/ParserClient.cpp data/parser/ParserClient.h + data/parser/ParseTypeUsage.cpp + data/parser/ParseTypeUsage.h data/parser/ParseVariable.cpp data/parser/ParseVariable.h diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index d0cda02c..5906f22f 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -7,6 +7,7 @@ #include "data/location/TokenLocationFile.h" #include "data/location/TokenLocationLine.h" #include "data/parser/ParseLocation.h" +#include "data/parser/ParseTypeUsage.h" #include "data/parser/ParseVariable.h" #include "data/type/DataType.h" #include "utility/logging/logging.h" @@ -105,30 +106,30 @@ void Storage::onFieldParsed(const ParseLocation& location, const ParseVariable& } void Storage::onFunctionParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters ) { log("function", fullName, location); Node* node = m_graph.createNodeHierarchyWithDistinctSignature( fullName, - ParserClient::functionSignatureStr(returnType, fullName, parameters, false) + ParserClient::functionSignatureStr(returnType.type, fullName, parameters, false) ); node->setType(Node::NODE_FUNCTION); - addTypeEdge(node, Edge::EDGE_RETURN_TYPE_OF, returnType); - for (const ParseVariable& parameter : parameters) - { - addTypeEdge(node, Edge::EDGE_PARAMETER_TYPE_OF, parameter.type); - } - addTokenLocation(node, location); + + addTypeEdge(node, Edge::EDGE_RETURN_TYPE_OF, returnType); + for (const ParseTypeUsage& parameter : parameters) + { + addTypeEdge(node, Edge::EDGE_PARAMETER_TYPE_OF, parameter); + } } void Storage::onMethodParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters, AccessType access, AbstractionType abstraction, + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters, AccessType access, AbstractionType abstraction, bool isConst, bool isStatic ) { @@ -136,7 +137,7 @@ void Storage::onMethodParsed( Node* node = m_graph.createNodeHierarchyWithDistinctSignature( fullName, - ParserClient::functionSignatureStr(returnType, fullName, parameters, isConst) + ParserClient::functionSignatureStr(returnType.type, fullName, parameters, isConst) ); node->setType(Node::NODE_METHOD); @@ -150,13 +151,13 @@ void Storage::onMethodParsed( } node->setAccess(convertAccessType(access)); - addTypeEdge(node, Edge::EDGE_RETURN_TYPE_OF, returnType); - for (const ParseVariable& parameter : parameters) - { - addTypeEdge(node, Edge::EDGE_PARAMETER_TYPE_OF, parameter.type); - } - addTokenLocation(node, location); + + addTypeEdge(node, Edge::EDGE_RETURN_TYPE_OF, returnType); + for (const ParseTypeUsage& parameter : parameters) + { + addTypeEdge(node, Edge::EDGE_PARAMETER_TYPE_OF, parameter); + } } void Storage::onNamespaceParsed(const ParseLocation& location, const std::string& fullName) @@ -381,8 +382,29 @@ Edge* Storage::addTypeEdge(Node* node, Edge::EdgeType edgeType, const DataType& return edge; } +Edge* Storage::addTypeEdge(Node* node, Edge::EdgeType edgeType, const ParseTypeUsage& typeUsage) +{ + if (!typeUsage.location.isValid()) + { + return nullptr; + } + + const DataType& type = typeUsage.type; + Node* typeNode = m_graph.createNodeHierarchy(type.getRawTypeName()); + Edge* edge = m_graph.createEdge(edgeType, node, typeNode); + edge->addComponent(std::make_shared(type.getQualifierList(), type.getModifierStack())); + + addTokenLocation(edge, typeUsage.location); + return edge; +} + TokenLocation* Storage::addTokenLocation(Token* token, const ParseLocation& loc) { + if (!loc.isValid()) + { + return nullptr; + } + TokenLocation* location = m_locationCollection.addTokenLocation( token->getId(), loc.filePath, loc.startLineNumber, loc.startColumnNumber, diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 6d0bc95b..fefc8f7d 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -36,12 +36,12 @@ public: virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access); virtual void onFunctionParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters ); virtual void onMethodParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters, AccessType access, AbstractionType abstraction, + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters, AccessType access, AbstractionType abstraction, bool isConst, bool isStatic ); @@ -73,6 +73,7 @@ public: private: Edge::AccessType convertAccessType(ParserClient::AccessType access) const; Edge* addTypeEdge(Node* node, Edge::EdgeType edgeType, const DataType& type); + Edge* addTypeEdge(Node* node, Edge::EdgeType edgeType, const ParseTypeUsage& typeUsage); TokenLocation* addTokenLocation(Token* token, const ParseLocation& location); void log(std::string type, std::string str, const ParseLocation& location) const; diff --git a/src/lib/data/parser/ParseLocation.cpp b/src/lib/data/parser/ParseLocation.cpp index e55cf65a..818bcf99 100644 --- a/src/lib/data/parser/ParseLocation.cpp +++ b/src/lib/data/parser/ParseLocation.cpp @@ -12,3 +12,8 @@ ParseLocation::ParseLocation( , endColumnNumber(endColumnNumber) { } + +bool ParseLocation::isValid() const +{ + return startLineNumber > 0 && endLineNumber >= startLineNumber; +} diff --git a/src/lib/data/parser/ParseLocation.h b/src/lib/data/parser/ParseLocation.h index 7c58832e..559db785 100644 --- a/src/lib/data/parser/ParseLocation.h +++ b/src/lib/data/parser/ParseLocation.h @@ -10,6 +10,8 @@ struct ParseLocation unsigned int startLineNumber, unsigned int startColumnNumber, unsigned int endLineNumber, unsigned int endColumnNumber); + bool isValid() const; + const std::string filePath; unsigned int startLineNumber; unsigned int startColumnNumber; diff --git a/src/lib/data/parser/ParseTypeUsage.cpp b/src/lib/data/parser/ParseTypeUsage.cpp new file mode 100644 index 00000000..16794c9f --- /dev/null +++ b/src/lib/data/parser/ParseTypeUsage.cpp @@ -0,0 +1,7 @@ +#include "data/parser/ParseTypeUsage.h" + +ParseTypeUsage::ParseTypeUsage(const ParseLocation& location, const DataType& type) + : location(location) + , type(type) +{ +} diff --git a/src/lib/data/parser/ParseTypeUsage.h b/src/lib/data/parser/ParseTypeUsage.h new file mode 100644 index 00000000..76c1f1d0 --- /dev/null +++ b/src/lib/data/parser/ParseTypeUsage.h @@ -0,0 +1,15 @@ +#ifndef PARSE_TYPE_USAGE_H +#define PARSE_TYPE_USAGE_H + +#include "data/type/DataType.h" +#include "data/parser/ParseLocation.h" + +struct ParseTypeUsage +{ + ParseTypeUsage(const ParseLocation& location, const DataType& type); + + const ParseLocation location; + const DataType type; +}; + +#endif // PARSE_TYPE_USAGE_H diff --git a/src/lib/data/parser/ParseVariable.cpp b/src/lib/data/parser/ParseVariable.cpp index fe6894b8..a0521030 100644 --- a/src/lib/data/parser/ParseVariable.cpp +++ b/src/lib/data/parser/ParseVariable.cpp @@ -1,6 +1,5 @@ #include "data/parser/ParseVariable.h" - ParseVariable::ParseVariable(const DataType& type, const std::string& fullName, bool isStatic) : type(type) , fullName(fullName) diff --git a/src/lib/data/parser/ParserClient.cpp b/src/lib/data/parser/ParserClient.cpp index bf0e89cc..d9be5d78 100644 --- a/src/lib/data/parser/ParserClient.cpp +++ b/src/lib/data/parser/ParserClient.cpp @@ -3,6 +3,7 @@ #include #include "data/parser/ParseLocation.h" +#include "data/parser/ParseTypeUsage.h" #include "data/parser/ParseVariable.h" #include "data/type/DataType.h" @@ -60,22 +61,18 @@ std::string ParserClient::addLocationSuffix(const std::string& str, const ParseL return ss.str(); } -std::string ParserClient::variableStr(const ParseVariable& variable, bool withName) +std::string ParserClient::variableStr(const ParseVariable& variable) { - std::string str = variable.type.getFullTypeName(); - if (withName) - { - str += " " + variable.fullName; - } + std::string str = variable.type.getFullTypeName() + " " + variable.fullName; return addStaticPrefix(str, variable.isStatic); } -std::string ParserClient::parameterStr(const std::vector parameters, bool withName) +std::string ParserClient::parameterStr(const std::vector parameters) { std::string str = "("; for (size_t i = 0; i < parameters.size(); i++) { - str += variableStr(parameters[i], withName); + str += parameters[i].type.getFullTypeName(); if (i < parameters.size() - 1) { str += ", "; @@ -87,7 +84,7 @@ std::string ParserClient::parameterStr(const std::vector paramete std::string ParserClient::functionStr( const DataType& returnType, const std::string& fullName, - const std::vector& parameters, + const std::vector& parameters, bool isConst ){ return addConstPrefix( @@ -100,10 +97,10 @@ std::string ParserClient::functionStr( std::string ParserClient::functionSignatureStr( const DataType& returnType, const std::string& fullName, - const std::vector& parameters, + const std::vector& parameters, bool isConst ){ - return addConstPrefix(fullName + parameterStr(parameters, false), isConst, false); + return addConstPrefix(fullName + parameterStr(parameters), isConst, false); } ParserClient::ParserClient() diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h index c38692bd..871e91a3 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -5,6 +5,7 @@ #include struct ParseLocation; +struct ParseTypeUsage; struct ParseVariable; class DataType; @@ -29,18 +30,18 @@ public: static std::string addStaticPrefix(const std::string& str, bool isStatic); static std::string addConstPrefix(const std::string& str, bool isConst, bool atFront); static std::string addLocationSuffix(const std::string& str, const ParseLocation& location); - static std::string variableStr(const ParseVariable& variable, bool withName = true); - static std::string parameterStr(const std::vector parameters, bool withName = true); + static std::string variableStr(const ParseVariable& variable); + static std::string parameterStr(const std::vector parameters); static std::string functionStr( const DataType& returnType, const std::string& fullName, - const std::vector& parameters, + const std::vector& parameters, bool isConst ); static std::string functionSignatureStr( const DataType& returnType, const std::string& fullName, - const std::vector& parameters, + const std::vector& parameters, bool isConst ); @@ -57,11 +58,11 @@ public: virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) = 0; virtual void onFunctionParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters) = 0; + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters) = 0; virtual void onMethodParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters, AccessType access, AbstractionType abstraction, + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters, AccessType access, AbstractionType abstraction, bool isConst, bool isStatic) = 0; virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName) = 0; diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp index f6df2c13..4385c39b 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.cpp +++ b/src/lib/data/parser/cxx/ASTVisitor.cpp @@ -3,6 +3,7 @@ #include "data/parser/cxx/ASTBodyVisitor.h" #include "data/parser/cxx/utilityCxx.h" #include "data/parser/ParseLocation.h" +#include "data/parser/ParseTypeUsage.h" #include "data/parser/ParseVariable.h" #include "data/type/DataType.h" @@ -139,7 +140,7 @@ bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration) m_client->onFunctionParsed( getParseLocation(declaration->getSourceRange()), declaration->getQualifiedNameAsString(), - utility::qualTypeToDataType(declaration->getReturnType()), + getParseTypeUsageOfReturnType(declaration), getParameters(declaration) ); @@ -170,7 +171,7 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) m_client->onMethodParsed( getParseLocation(declaration->getSourceRange()), declaration->getQualifiedNameAsString(), - utility::qualTypeToDataType(declaration->getReturnType()), + getParseTypeUsageOfReturnType(declaration), getParameters(declaration), convertAccessType(declaration->getAccess()), abstraction, @@ -285,6 +286,11 @@ bool ASTVisitor::hasValidLocation(const clang::Decl* declaration) const ParseLocation ASTVisitor::getParseLocation(const clang::SourceRange& sourceRange) const { + if (sourceRange.isInvalid()) + { + return ParseLocation("", 0, 0, 0, 0); + } + const clang::SourceManager& sourceManager = m_context->getSourceManager(); const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(sourceRange.getBegin()); @@ -299,10 +305,16 @@ ParseLocation ASTVisitor::getParseLocation(const clang::SourceRange& sourceRange ); } +ParseTypeUsage ASTVisitor::getParseTypeUsage(clang::ValueDecl* declaration) const +{ + return ParseTypeUsage( + getParseLocation(declaration->getSourceRange()), + utility::qualTypeToDataType(declaration->getType()) + ); +} + ParseVariable ASTVisitor::getParseVariable(clang::ValueDecl* declaration) const { - clang::QualType qualType = declaration->getType(); - bool isStatic = false; if (clang::isa(declaration)) { @@ -311,19 +323,43 @@ ParseVariable ASTVisitor::getParseVariable(clang::ValueDecl* declaration) const } return ParseVariable( - utility::qualTypeToDataType(qualType), + utility::qualTypeToDataType(declaration->getType()), declaration->getQualifiedNameAsString(), isStatic ); } -std::vector ASTVisitor::getParameters(clang::FunctionDecl* declaration) const +ParseTypeUsage ASTVisitor::getParseTypeUsageOfReturnType(clang::FunctionDecl* declaration) const { - std::vector parameters; + // TODO: use FunctionDecl::getReturnTypeSourceRange() in newer clang version + clang::SourceRange range; + const clang::TypeSourceInfo *TSI = declaration->getTypeSourceInfo(); + if (TSI) + { + clang::FunctionTypeLoc FTL = TSI->getTypeLoc().IgnoreParens().getAs(); + if (FTL) + { + // Skip self-referential return types. + range = clang::SourceRange( + FTL.getReturnLoc().getLocStart(), + declaration->getNameInfo().getLocStart().getLocWithOffset(-2) + ); + } + } + + return ParseTypeUsage( + getParseLocation(range), + utility::qualTypeToDataType(declaration->getReturnType()) + ); +} + +std::vector ASTVisitor::getParameters(clang::FunctionDecl* declaration) const +{ + std::vector parameters; for (unsigned i = 0; i < declaration->getNumParams(); i++) { - parameters.push_back(getParseVariable(declaration->getParamDecl(i))); + parameters.push_back(getParseTypeUsage(declaration->getParamDecl(i))); } return parameters; diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h index 145922c1..04273dfe 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.h +++ b/src/lib/data/parser/cxx/ASTVisitor.h @@ -45,8 +45,10 @@ public: private: bool hasValidLocation(const clang::Decl* declaration) const; ParseLocation getParseLocation(const clang::SourceRange& sourceRange) const; + ParseTypeUsage getParseTypeUsage(clang::ValueDecl* declaration) const; ParseVariable getParseVariable(clang::ValueDecl* declaration) const; - std::vector getParameters(clang::FunctionDecl* declaration) const; + ParseTypeUsage getParseTypeUsageOfReturnType(clang::FunctionDecl* declaration) const; + std::vector getParameters(clang::FunctionDecl* declaration) const; DataType qualTypeToDataType(clang::QualType qualType); std::string getTypeName(const clang::QualType& qualType) const; ParserClient::AccessType convertAccessType(clang::AccessSpecifier) const; diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index b6742ff4..df9f99ec 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -5,6 +5,7 @@ #include "data/parser/cxx/CxxParser.h" #include "data/parser/ParseLocation.h" #include "data/parser/ParserClient.h" +#include "data/parser/ParseTypeUsage.h" #include "data/parser/ParseVariable.h" #include "utility/text/TextAccess.h" @@ -197,7 +198,7 @@ public: ); TS_ASSERT_EQUALS(client->functions.size(), 1); - TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a) <1:1 4:1>"); + TS_ASSERT_EQUALS(client->functions[0], "int ceil(float) <1:1 4:1>"); } void test_cxx_parser_finds_function_in_anonymous_namespace() @@ -210,7 +211,7 @@ public: ); TS_ASSERT_EQUALS(client->functions.size(), 1); - TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int a, int b) <3:2 3:22>"); + TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int, int) <3:2 3:22>"); } void test_cxx_parser_finds_method_declaration() @@ -674,6 +675,49 @@ public: TS_ASSERT_EQUALS(client->calls[1], "main -> App::operator+ <11:2 11:8>"); } + void test_cxx_parser_finds_return_type_use_in_function() + { + std::shared_ptr client = parseCode( + "double PI()\n" + "{\n" + " return 3.14159265359;\n" + "}\n" + ); + + TS_ASSERT_EQUALS(client->typeUses.size(), 1); + TS_ASSERT_EQUALS(client->typeUses[0], "double <1:1 1:6>"); + } + + void test_cxx_parser_finds_return_and_parameter_type_uses_in_function() + { + std::shared_ptr client = parseCode( + "int ceil(float a)\n" + "{\n" + " return static_cast(a) + 1;\n" + "}\n" + ); + + TS_ASSERT_EQUALS(client->typeUses.size(), 2); + TS_ASSERT_EQUALS(client->typeUses[0], "int <1:1 1:3>"); + TS_ASSERT_EQUALS(client->typeUses[1], "float <1:10 1:16>"); + } + + void test_cxx_parser_finds_parameter_type_uses_in_constructor() + { + std::shared_ptr client = parseCode( + "class A\n" + "{\n" + " A(int a, bool b, float c, int d);\n" + "};\n" + ); + + TS_ASSERT_EQUALS(client->typeUses.size(), 4); + TS_ASSERT_EQUALS(client->typeUses[0], "int <3:4 3:8>"); + TS_ASSERT_EQUALS(client->typeUses[1], "_Bool <3:11 3:16>"); + TS_ASSERT_EQUALS(client->typeUses[2], "float <3:19 3:25>"); + TS_ASSERT_EQUALS(client->typeUses[3], "int <3:28 3:32>"); + } + void test_cxx_parser_parses_multiple_files() { std::shared_ptr client = std::make_shared(); @@ -730,24 +774,36 @@ private: } virtual void onFunctionParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters ){ - std::string str = functionStr(returnType, fullName, parameters, false); + std::string str = functionStr(returnType.type, fullName, parameters, false); functions.push_back(addLocationSuffix(str, location)); + + addTypeUse(returnType); + for (const ParseTypeUsage& parameter : parameters) + { + addTypeUse(parameter); + } } virtual void onMethodParsed( - const ParseLocation& location, const std::string& fullName, const DataType& returnType, - const std::vector& parameters, AccessType access, AbstractionType abstraction, + const ParseLocation& location, const std::string& fullName, const ParseTypeUsage& returnType, + const std::vector& parameters, AccessType access, AbstractionType abstraction, bool isConst, bool isStatic ) { - std::string str = functionStr(returnType, fullName, parameters, isConst); + std::string str = functionStr(returnType.type, fullName, parameters, isConst); str = addStaticPrefix(addAbstractionPrefix(str, abstraction), isStatic); str = addAccessPrefix(str, access); str = addLocationSuffix(str, location); methods.push_back(str); + + addTypeUse(returnType); + for (const ParseTypeUsage& parameter : parameters) + { + addTypeUse(parameter); + } } virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName) @@ -790,6 +846,16 @@ private: std::vector structs; std::vector inheritances; std::vector calls; + std::vector typeUses; + + private: + void addTypeUse(const ParseTypeUsage& use) + { + if (use.location.isValid()) + { + typeUses.push_back(addLocationSuffix(use.type.getFullTypeName(), use.location)); + } + } }; std::shared_ptr parseCode(std::string code) const