diff --git a/bin/test/data/log/test_log.txt b/bin/test/data/log/test_log.txt index 2bfb2e81..bb9979c1 100644 --- a/bin/test/data/log/test_log.txt +++ b/bin/test/data/log/test_log.txt @@ -1,4 +1,26 @@ ConfigManager.cpp ERROR: value path/to/nowhere is not present in config. +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: class: A +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 Storage.cpp INFO: class: A Storage.cpp INFO: method: A::A Storage.cpp INFO: global usage: A::A -> A::count @@ -11,6 +33,7 @@ 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 @@ -31,27 +54,6 @@ SearchMatch.cpp INFO: 237 A::A ^^^^ -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::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: 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 -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 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. @@ -67,7 +69,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 @@ -95,6 +96,9 @@ Storage.cpp INFO: inheritance: ClassB : ClassA Storage.cpp INFO: struct: StructA Storage.cpp INFO: struct: StructB Storage.cpp INFO: inheritance: StructB : StructA +Storage.cpp INFO: method: A::isMethod +Storage.cpp INFO: method: B::isMethod +Storage.cpp INFO: override: A::isMethod -> B::isMethod < 0:0 0:0> Storage.cpp INFO: function: isTrue Storage.cpp INFO: function: func Storage.cpp INFO: call: isTrue -> func @@ -120,6 +124,7 @@ Storage.cpp INFO: call: main -> isTrue Storage.cpp INFO: function: isTrue Storage.cpp INFO: function: main Storage.cpp INFO: call: main -> isTrue +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/QtGraphEdge.cpp b/src/app/qt/view/graphElements/QtGraphEdge.cpp index 4d0d76b5..a51b0294 100644 --- a/src/app/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/app/qt/view/graphElements/QtGraphEdge.cpp @@ -446,6 +446,7 @@ void QtGraphEdge::updateLine() color = QColor("#62B29D"); break; case Edge::EDGE_INHERITANCE: + case Edge::EDGE_OVERRIDE: color = QColor("#CC5E89"); break; case Edge::EDGE_AGGREGATION: diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index f0e86fc3..c03f6bc8 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -357,6 +357,18 @@ Id Storage::onInheritanceParsed( return edge->getId(); } +Id Storage::onMethodOverrideParsed(const ParseFunction& base, const ParseFunction& overrider) +{ + log("override", base.getFullName() + " -> " + overrider.getFullName(), ParseLocation()); + + Node* baseNode = addNodeHierarchyWithDistinctSignature(Node::NODE_UNDEFINED_FUNCTION, base); + Node* overriderNode = addNodeHierarchyWithDistinctSignature(Node::NODE_UNDEFINED_FUNCTION, overrider); + + Edge* edge = m_graph.createEdge(Edge::EDGE_OVERRIDE, baseNode, overriderNode); + + return edge->getId(); +} + Id Storage::onCallParsed(const ParseLocation& location, const ParseFunction& caller, const ParseFunction& callee) { log("call", caller.getFullName() + " -> " + callee.getFullName(), location); diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 1fd233e2..27c9c1ed 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -63,6 +63,7 @@ public: virtual Id onInheritanceParsed( const ParseLocation& location, const std::vector& nameHierarchy, const std::vector& baseNameHierarchy, AccessType access); + virtual Id onMethodOverrideParsed(const ParseFunction& base, const ParseFunction& overrider); virtual Id onCallParsed( const ParseLocation& location, const ParseFunction& caller, const ParseFunction& callee); virtual Id onCallParsed( diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp index f7ebd4a5..cc4d194a 100644 --- a/src/lib/data/graph/Edge.cpp +++ b/src/lib/data/graph/Edge.cpp @@ -125,6 +125,8 @@ std::string Edge::getTypeString(EdgeType type) const return "type_usage"; case EDGE_INHERITANCE: return "inheritance"; + case EDGE_OVERRIDE: + return "override"; case EDGE_CALL: return "call"; case EDGE_USAGE: @@ -215,6 +217,14 @@ bool Edge::checkType() const } return true; + case EDGE_OVERRIDE: + if (!m_from->isType(Node::NODE_UNDEFINED_FUNCTION | Node::NODE_METHOD) || + !m_to->isType(Node::NODE_UNDEFINED_FUNCTION | Node::NODE_METHOD)) + { + break; + } + return true; + case EDGE_CALL: if (!m_from->isType(variableMask | functionMask) || !m_to->isType(functionMask)) { diff --git a/src/lib/data/graph/Edge.h b/src/lib/data/graph/Edge.h index be7850cf..e438fe7c 100644 --- a/src/lib/data/graph/Edge.h +++ b/src/lib/data/graph/Edge.h @@ -25,13 +25,14 @@ public: EDGE_USAGE = 0x20, EDGE_CALL = 0x40, EDGE_INHERITANCE = 0x80, - EDGE_TYPEDEF_OF = 0x100, - EDGE_TEMPLATE_PARAMETER_OF = 0x200, - EDGE_TEMPLATE_ARGUMENT_OF = 0x400, - EDGE_TEMPLATE_DEFAULT_ARGUMENT_OF = 0x800, - EDGE_TEMPLATE_SPECIALIZATION_OF = 0x1000, + EDGE_OVERRIDE = 0x100, + EDGE_TYPEDEF_OF = 0x200, + EDGE_TEMPLATE_PARAMETER_OF = 0x400, + EDGE_TEMPLATE_ARGUMENT_OF = 0x800, + EDGE_TEMPLATE_DEFAULT_ARGUMENT_OF = 0x1000, + EDGE_TEMPLATE_SPECIALIZATION_OF = 0x2000, - EDGE_AGGREGATION = 0x2000 + EDGE_AGGREGATION = 0x4000 }; Edge(EdgeType type, Node* from, Node* to); diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h index 33a0f277..ef42dc6b 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -82,6 +82,7 @@ public: virtual Id onInheritanceParsed( const ParseLocation& location, const std::vector& nameHierarchy, const std::vector& baseNameHierarchy, AccessType access) = 0; + virtual Id onMethodOverrideParsed(const ParseFunction& base, const ParseFunction& overrider) = 0; virtual Id onCallParsed( const ParseLocation& location, const ParseFunction& caller, const ParseFunction& callee) = 0; virtual Id onCallParsed( diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp index d873ffd2..80d07e3a 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.cpp +++ b/src/lib/data/parser/cxx/ASTVisitor.cpp @@ -180,14 +180,22 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) abstraction = ParserClient::ABSTRACTION_VIRTUAL; } + ParseFunction parseFunction = getParseFunction(declaration); + m_client->onMethodParsed( getParseLocationForNamedDecl(declaration), - getParseFunction(declaration), + parseFunction, convertAccessType(declaration->getAccess()), abstraction, getParseLocationOfFunctionBody(declaration) ); + for (clang::CXXMethodDecl::method_iterator it = declaration->begin_overridden_methods(); + it != declaration->end_overridden_methods(); it++) + { + m_client->onMethodOverrideParsed(getParseFunction(*it), parseFunction); + } + if (declaration->hasBody() && declaration->getBody() != NULL && declaration->isThisDeclarationADefinition()) { ASTBodyVisitor bodyVisitor(this, declaration); @@ -725,7 +733,7 @@ ParseTypeUsage ASTVisitor::getParseTypeUsage(clang::TypeLoc typeLoc, const clang return ParseTypeUsage(parseLocation, dataType); } -ParseTypeUsage ASTVisitor::getParseTypeUsageOfReturnType(clang::FunctionDecl* declaration) const +ParseTypeUsage ASTVisitor::getParseTypeUsageOfReturnType(const clang::FunctionDecl* declaration) const { clang::TypeLoc typeLoc; @@ -742,13 +750,13 @@ ParseTypeUsage ASTVisitor::getParseTypeUsageOfReturnType(clang::FunctionDecl* de return getParseTypeUsage(typeLoc, declaration->getReturnType()); } -std::vector ASTVisitor::getParameters(clang::FunctionDecl* declaration) const +std::vector ASTVisitor::getParameters(const clang::FunctionDecl* declaration) const { std::vector parameters; for (unsigned i = 0; i < declaration->getNumParams(); i++) { - clang::ParmVarDecl* paramDecl = declaration->getParamDecl(i); + const clang::ParmVarDecl* paramDecl = declaration->getParamDecl(i); if (paramDecl->getTypeSourceInfo()) { parameters.push_back(getParseTypeUsage(paramDecl->getTypeSourceInfo()->getTypeLoc(), paramDecl->getType())); @@ -758,13 +766,13 @@ std::vector ASTVisitor::getParameters(clang::FunctionDecl* decla return parameters; } -ParseVariable ASTVisitor::getParseVariable(clang::DeclaratorDecl* declaration) const +ParseVariable ASTVisitor::getParseVariable(const clang::DeclaratorDecl* declaration) const { bool isStatic = false; std::vector hameHierarchy = utility::getDeclNameHierarchy(declaration); if (clang::isa(declaration)) { - clang::VarDecl* varDecl = clang::dyn_cast(declaration); + const clang::VarDecl* varDecl = clang::dyn_cast(declaration); isStatic = varDecl->isStaticDataMember() || varDecl->getStorageClass() == clang::SC_Static; } else if (clang::isa(declaration)) @@ -779,14 +787,14 @@ ParseVariable ASTVisitor::getParseVariable(clang::DeclaratorDecl* declaration) c ); } -ParseFunction ASTVisitor::getParseFunction(clang::FunctionDecl* declaration) const +ParseFunction ASTVisitor::getParseFunction(const clang::FunctionDecl* declaration) const { bool isStatic = false; bool isConst = false; if (clang::isa(declaration)) { - clang::CXXMethodDecl* methodDecl = clang::dyn_cast(declaration); + const clang::CXXMethodDecl* methodDecl = clang::dyn_cast(declaration); isStatic = methodDecl->isStatic(); isConst = methodDecl->isConst(); } diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h index dd3951ea..1f5a5815 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.h +++ b/src/lib/data/parser/cxx/ASTVisitor.h @@ -68,11 +68,11 @@ private: ParseLocation getParseLocationOfRecordBody(clang::CXXRecordDecl* decl) const; ParseTypeUsage getParseTypeUsage(clang::TypeLoc typeLoc, const clang::QualType& type) const; - ParseTypeUsage getParseTypeUsageOfReturnType(clang::FunctionDecl* declaration) const; - std::vector getParameters(clang::FunctionDecl* declaration) const; + ParseTypeUsage getParseTypeUsageOfReturnType(const clang::FunctionDecl* declaration) const; + std::vector getParameters(const clang::FunctionDecl* declaration) const; - ParseVariable getParseVariable(clang::DeclaratorDecl* declaration) const; - ParseFunction getParseFunction(clang::FunctionDecl* declaration) const; + ParseVariable getParseVariable(const clang::DeclaratorDecl* declaration) const; + ParseFunction getParseFunction(const clang::FunctionDecl* declaration) const; clang::ASTContext* m_context; ParserClient* m_client; diff --git a/src/lib/data/parser/cxx/utilityCxx.cpp b/src/lib/data/parser/cxx/utilityCxx.cpp index 08e2887e..b7d7d43e 100644 --- a/src/lib/data/parser/cxx/utilityCxx.cpp +++ b/src/lib/data/parser/cxx/utilityCxx.cpp @@ -105,7 +105,7 @@ namespace utility return DataType(typeNameHerarchy, qualifierList, modifierStack); } - std::vector getDeclNameHierarchy(clang::Decl* declaration) + std::vector getDeclNameHierarchy(const clang::Decl* declaration) { std::vector contextNameHierarchy; if (declaration) @@ -114,7 +114,7 @@ namespace utility if (clang::isa(declaration)) { - declName = getDeclName(clang::dyn_cast(declaration)); + declName = getDeclName(clang::dyn_cast(declaration)); } else { @@ -133,11 +133,11 @@ namespace utility return contextNameHierarchy; } - std::vector getContextNameHierarchy(clang::DeclContext* declContext) + std::vector getContextNameHierarchy(const clang::DeclContext* declContext) { std::vector contextNameHierarchy; - clang::DeclContext* parentContext = declContext->getParent(); + const clang::DeclContext* parentContext = declContext->getParent(); if (parentContext) { contextNameHierarchy = getContextNameHierarchy(parentContext); @@ -145,7 +145,7 @@ namespace utility if (clang::isa(declContext)) { - std::string declName = getDeclName(clang::dyn_cast(declContext)); + std::string declName = getDeclName(clang::dyn_cast(declContext)); if (declName != "") { contextNameHierarchy.push_back(declName); @@ -154,7 +154,7 @@ namespace utility return contextNameHierarchy; } - std::string getDeclName(clang::NamedDecl* declaration) + std::string getDeclName(const clang::NamedDecl* declaration) { std::string declName = declaration->getNameAsString(); diff --git a/src/lib/data/parser/cxx/utilityCxx.h b/src/lib/data/parser/cxx/utilityCxx.h index 07d0af7a..2b50cd4c 100644 --- a/src/lib/data/parser/cxx/utilityCxx.h +++ b/src/lib/data/parser/cxx/utilityCxx.h @@ -15,9 +15,9 @@ namespace utility { DataType qualTypeToDataType(clang::QualType qualType); - std::vector getDeclNameHierarchy(clang::Decl* declaration); - std::vector getContextNameHierarchy(clang::DeclContext* declaration); - std::string getDeclName(clang::NamedDecl* declaration); + std::vector getDeclNameHierarchy(const clang::Decl* declaration); + std::vector getContextNameHierarchy(const clang::DeclContext* declaration); + std::string getDeclName(const clang::NamedDecl* declaration); std::vector getTemplateSpecializationParentNameHierarchy(clang::ClassTemplateSpecializationDecl* declaration); DataType templateArgumentToDataType(const clang::TemplateArgument& argument); } diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index 2c79b3bd..6b427180 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -599,6 +599,84 @@ public: TS_ASSERT_EQUALS(client->inheritances[1], "C : private B <5:4 5:12>"); } + void test_cxx_parser_finds_method_override_when_virtual() + { + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};" + ); + + TS_ASSERT_EQUALS(client->overrides.size(), 1); + TS_ASSERT_EQUALS(client->overrides[0], "void A::foo() -> void B::foo()"); + } + + void test_cxx_parser_finds_no_method_override_when_not_virtual() + { + std::shared_ptr client = parseCode( + "class A {\n" + " void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};" + ); + + TS_ASSERT_EQUALS(client->overrides.size(), 0); + } + + void test_cxx_parser_finds_all_method_overrides() + { + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " void foo();\n" + "};\n" + "class C : public B {\n" + " void foo();\n" + "};" + ); + + TS_ASSERT_EQUALS(client->overrides.size(), 2); + TS_ASSERT_EQUALS(client->overrides[0], "void A::foo() -> void B::foo()"); + TS_ASSERT_EQUALS(client->overrides[1], "void B::foo() -> void C::foo()"); + } + + void test_cxx_parser_finds_no_method_overrides_on_different_signatures() + { + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo(int a);\n" + "};\n" + "class B : public A {\n" + " int foo(int a, int b);\n" + "};\n" + ); + + TS_ASSERT_EQUALS(client->overrides.size(), 0); + } + + void test_cxx_parser_finds_method_overrides_on_different_return_types() + { + std::shared_ptr client = parseCode( + "class A {\n" + " virtual void foo();\n" + "};\n" + "class B : public A {\n" + " int foo();\n" + "};\n" + ); + + TS_ASSERT_EQUALS(client->overrides.size(), 1); + TS_ASSERT_EQUALS(client->overrides[0], "void A::foo() -> int B::foo()"); + TS_ASSERT_EQUALS(client->errors.size(), 1); + } + void test_cxx_parser_finds_call_in_function() { std::shared_ptr client = parseCode( @@ -1678,6 +1756,12 @@ private: return 0; } + virtual Id onMethodOverrideParsed(const ParseFunction& base, const ParseFunction& overrider) + { + overrides.push_back(functionStr(base) + " -> " + functionStr(overrider)); + return 0; + } + virtual Id onCallParsed( const ParseLocation& location, const ParseFunction& caller, const ParseFunction& callee) { @@ -1812,6 +1896,7 @@ private: std::vector structs; std::vector inheritances; + std::vector overrides; std::vector calls; std::vector usages; // for variables std::vector typeUses; // for types diff --git a/src/test/GraphFilterConductorTestSuite.h b/src/test/GraphFilterConductorTestSuite.h index 14b9bf9b..108ee899 100644 --- a/src/test/GraphFilterConductorTestSuite.h +++ b/src/test/GraphFilterConductorTestSuite.h @@ -100,10 +100,10 @@ public: "7 nodes: " "class:A field:A::count undefined_type:int undefined_type:void class:B function:main " "undefined_function:B::B\n" - "13 edges: " + "14 edges: " "child:A->A::count aggregation:A->int aggregation:A->void type_use:A::count->int inheritance:B->A " - "aggregation:B->void 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" + "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" ); } diff --git a/src/test/StorageTestSuite.h b/src/test/StorageTestSuite.h index 258bc4d7..5f47ec76 100644 --- a/src/test/StorageTestSuite.h +++ b/src/test/StorageTestSuite.h @@ -407,6 +407,25 @@ public: TS_ASSERT(isValidLocation(locations[0], 5)); } + void test_storage_saves_method_override() + { + TestStorage storage; + + ParseFunction a(typeUsage("void"), utility::splitToVector("A::isMethod", "::"), parameters("bool")); + ParseFunction b(typeUsage("void"), utility::splitToVector("B::isMethod", "::"), parameters("bool")); + + storage.onMethodParsed(validLocation(9), a, ParserClient::ACCESS_PRIVATE, ParserClient::ABSTRACTION_VIRTUAL, validLocation(4)); + storage.onMethodParsed(validLocation(7), b, ParserClient::ACCESS_PRIVATE, ParserClient::ABSTRACTION_NONE, validLocation(3)); + + Id id = storage.onMethodOverrideParsed(a, b); + + Edge* edge = storage.getEdgeWithId(id); + TS_ASSERT(edge); + TS_ASSERT_EQUALS(edge->getType(), Edge::EDGE_OVERRIDE); + TS_ASSERT_EQUALS(edge->getFrom()->getFullName(), "A::isMethod"); + TS_ASSERT_EQUALS(edge->getTo()->getFullName(), "B::isMethod"); + } + void test_storage_saves_call() { TestStorage storage;