logic: improved recording of template member specializations

* implemented recording template member specialitation relations for fields, static variables, member classes and enums
* added windows only tests that index whole projects (Bullet & Box2D) to prevent regression
This commit is contained in:
mlangkabel
2017-09-13 18:03:29 +02:00
parent 3f06724cad
commit 687bfad4a6
222 changed files with 95747 additions and 22 deletions
+10
View File
@@ -97,6 +97,16 @@ std::vector<std::string> TextAccess::readFile(const FilePath& filePath)
}
srcFile.close();
if (!result.empty())
{
std::string last = result.back().substr(0, result.back().size() - 1);
result.pop_back();
if (!last.empty())
{
result.push_back(last);
}
}
return result;
}
@@ -204,7 +204,7 @@ DEF_TRAVERSE_TYPE(TypeLoc, {}, {})
// additionally: skip implicit CXXRecordDecls (this does not skip template specializations).
bool CxxAstVisitor::TraverseCXXRecordDecl(clang::CXXRecordDecl *d)
{
if (utility::isImplicit(d))
if (utility::isImplicit(d) && d->getMemberSpecializationInfo() == nullptr)
{
return true;
}
@@ -142,14 +142,35 @@ void CxxAstVisitorComponentIndexer::visitTagDecl(clang::TagDecl* d)
definitionKind = utility::isImplicit(d) ? DEFINITION_IMPLICIT : DEFINITION_EXPLICIT;
}
const SymbolKind symbolKind = utility::convertTagKind(d->getTagKind());
m_client->recordSymbol(
getAstVisitor()->getDeclNameCache()->getValue(d),
utility::convertTagKind(d->getTagKind()),
symbolKind,
getParseLocation(d->getLocation()),
getParseLocationOfTagDeclBody(d),
utility::convertAccessSpecifier(d->getAccess()),
definitionKind
);
if (clang::EnumDecl* enumDecl = clang::dyn_cast_or_null<clang::EnumDecl>(d))
{
recordTemplateMemberSpecialization(
enumDecl->getMemberSpecializationInfo(),
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation()),
symbolKind
);
}
if(clang::CXXRecordDecl* recordDecl = clang::dyn_cast_or_null<clang::CXXRecordDecl>(d))
{
recordTemplateMemberSpecialization(
recordDecl->getMemberSpecializationInfo(),
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation()),
symbolKind
);
}
}
}
@@ -205,6 +226,13 @@ void CxxAstVisitorComponentIndexer::visitVarDecl(clang::VarDecl* d)
utility::convertAccessSpecifier(d->getAccess()),
utility::isImplicit(d) ? DEFINITION_IMPLICIT : DEFINITION_EXPLICIT
);
recordTemplateMemberSpecialization(
d->getMemberSpecializationInfo(),
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation()),
symbolKind
);
}
}
}
@@ -220,6 +248,30 @@ void CxxAstVisitorComponentIndexer::visitFieldDecl(clang::FieldDecl* d)
utility::convertAccessSpecifier(d->getAccess()),
utility::isImplicit(d) ? DEFINITION_IMPLICIT : DEFINITION_EXPLICIT
);
if (clang::CXXRecordDecl* declaringRecordDecl = clang::dyn_cast_or_null<clang::CXXRecordDecl>(d->getParent()))
{
if (clang::CXXRecordDecl* declaringRecordTemplateDecl = declaringRecordDecl->getTemplateInstantiationPattern())
{
for (clang::FieldDecl* templateFieldDecl : declaringRecordTemplateDecl->fields())
{
if (d->getName() == templateFieldDecl->getName())
{
const NameHierarchy referencedName = getAstVisitor()->getDeclNameCache()->getValue(templateFieldDecl);
m_client->recordSymbol(referencedName, SYMBOL_FIELD, ACCESS_NONE, DEFINITION_NONE);
m_client->recordReference(
REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION,
referencedName,
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation())
);
break;
}
}
}
}
}
}
@@ -272,24 +324,12 @@ void CxxAstVisitorComponentIndexer::visitCXXMethodDecl(clang::CXXMethodDecl* d)
);
}
clang::MemberSpecializationInfo* memberSpecializationInfo = d->getMemberSpecializationInfo();
if (memberSpecializationInfo)
{
clang::NamedDecl* specializedNamedDecl = memberSpecializationInfo->getInstantiatedFrom();
if (clang::isa<clang::FunctionDecl>(specializedNamedDecl))
{
const NameHierarchy referencedName = getAstVisitor()->getDeclNameCache()->getValue(specializedNamedDecl);
m_client->recordSymbol(referencedName, SYMBOL_FUNCTION, ACCESS_NONE, DEFINITION_NONE);
m_client->recordReference(
REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION,
referencedName,
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation())
);
}
}
recordTemplateMemberSpecialization(
d->getMemberSpecializationInfo(),
getAstVisitor()->getDeclNameCache()->getValue(d),
getParseLocation(d->getLocation()),
SYMBOL_FUNCTION
);
}
}
@@ -643,6 +683,25 @@ void CxxAstVisitorComponentIndexer::visitConstructorInitializer(clang::CXXCtorIn
}
}
void CxxAstVisitorComponentIndexer::recordTemplateMemberSpecialization(
const clang::MemberSpecializationInfo* memberSpecializationInfo, const NameHierarchy& context, const ParseLocation& location, SymbolKind symbolKind)
{
if (memberSpecializationInfo != nullptr)
{
clang::NamedDecl* specializedNamedDecl = memberSpecializationInfo->getInstantiatedFrom();
const NameHierarchy referencedName = getAstVisitor()->getDeclNameCache()->getValue(specializedNamedDecl);
m_client->recordSymbol(referencedName, symbolKind, ACCESS_NONE, DEFINITION_NONE);
m_client->recordReference(
REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION,
referencedName,
context,
location
);
}
}
ParseLocation CxxAstVisitorComponentIndexer::getParseLocationOfTagDeclBody(clang::TagDecl* decl) const
{
return getAstVisitor()->getParseLocationOfTagDeclBody(decl);
@@ -54,6 +54,13 @@ private:
}
};
void recordTemplateMemberSpecialization(
const clang::MemberSpecializationInfo* memberSpecializationInfo,
const NameHierarchy& context,
const ParseLocation& location,
SymbolKind symbolKind
);
ParseLocation getParseLocationOfTagDeclBody(clang::TagDecl* decl) const;
ParseLocation getParseLocationOfFunctionBody(const clang::FunctionDecl* decl) const;
ParseLocation getParseLocation(const clang::SourceLocation& loc) const;
+2 -1
View File
@@ -8,8 +8,9 @@ add_files(
TestSuiteFixture.cpp
TestSuiteFixture.h
ConfigManagerTestSuite.h
CommandlineTestSuite.h
ConfigManagerTestSuite.h
CxxIndexSampleProjectsTestSuite.h
CxxParserTestSuite.h
CxxTypeNameTestSuite.h
FileManagerTestSuite.h
+307
View File
@@ -0,0 +1,307 @@
#include "cxxtest/TestSuite.h"
#include <fstream>
#include <iostream>
#include "settings/ApplicationSettings.h"
#include "utility/text/TextAccess.h"
#include "utility/utility.h"
#include "utility/utilityString.h"
#include "data/indexer/IndexerCommandCxxManual.h"
#include "data/parser/cxx/CxxParser.h"
#include "helper/TestFileRegister.h"
#include "helper/TestParserClient.h"
class CxxIndexSampleProjectsTestSuite : public CxxTest::TestSuite
{
public:
void test_index_box2d_project()
{
#ifdef _WIN32
const bool updateExpectedOutput = false;
processSourceFile("Box2D", FilePath("Box2D/Collision/b2BroadPhase.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2CollideCircle.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2CollideEdge.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2CollidePolygon.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2Collision.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2Distance.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2DynamicTree.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/b2TimeOfImpact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/Shapes/b2ChainShape.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/Shapes/b2CircleShape.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/Shapes/b2EdgeShape.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Collision/Shapes/b2PolygonShape.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2BlockAllocator.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2Draw.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2Math.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2Settings.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2StackAllocator.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Common/b2Timer.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2Body.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2ContactManager.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2Fixture.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2Island.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2World.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/b2WorldCallbacks.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2ChainAndCircleContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2ChainAndPolygonContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2CircleContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2Contact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2ContactSolver.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2EdgeAndCircleContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2EdgeAndPolygonContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2PolygonAndCircleContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Contacts/b2PolygonContact.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2DistanceJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2FrictionJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2GearJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2Joint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2MotorJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2MouseJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2PrismaticJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2PulleyJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2RevoluteJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2RopeJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2WeldJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Dynamics/Joints/b2WheelJoint.cpp"), updateExpectedOutput);
processSourceFile("Box2D", FilePath("Box2D/Rope/b2Rope.cpp"), updateExpectedOutput);
#endif
}
void test_index_bullet3_project()
{
#ifdef _WIN32
const bool updateExpectedOutput = false;
processSourceFile("Bullet3", FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvh.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Collision/BroadPhaseCollision/b3DynamicBvhBroadphase.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Collision/BroadPhaseCollision/b3OverlappingPairCache.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Collision/NarrowPhaseCollision/b3ConvexUtility.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Collision/NarrowPhaseCollision/b3CpuNarrowPhase.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Common/b3AlignedAllocator.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Common/b3Logging.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Common/b3Vector3.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Geometry/b3ConvexHullComputer.cpp"), updateExpectedOutput);
processSourceFile("Bullet3", FilePath("Bullet3Geometry/b3GeometryUtil.cpp"), updateExpectedOutput);
#endif
}
private:
class DumpParserClient : public ParserClient
{
public:
DumpParserClient()
: m_dump("")
{
}
virtual Id recordSymbol(
const NameHierarchy& symbolName, SymbolKind symbolKind,
AccessKind access, DefinitionKind definitionKind)
{
m_dump += symbolKindToString(symbolKind) + " " + addAccessPrefix(symbolName.getQualifiedNameWithSignature(), access) + "\n";
return 0;
}
virtual Id recordSymbol(
const NameHierarchy& symbolName, SymbolKind symbolKind,
const ParseLocation& location,
AccessKind access, DefinitionKind definitionKind)
{
m_dump += symbolKindToString(symbolKind) + " " + addLocationSuffix(addAccessPrefix(symbolName.getQualifiedNameWithSignature(), access) + " [" + location.filePath.fileName(), location) + "]\n";
return 0;
}
virtual Id recordSymbol(
const NameHierarchy& symbolName, SymbolKind symbolKind,
const ParseLocation& location, const ParseLocation& scopeLocation,
AccessKind access, DefinitionKind definitionKind)
{
m_dump += symbolKindToString(symbolKind) + " " + addLocationSuffix(addAccessPrefix(symbolName.getQualifiedNameWithSignature(), access) + " [" + location.filePath.fileName(), location, scopeLocation) + "]\n";
return 0;
}
void recordReference(
ReferenceKind referenceKind, const NameHierarchy& referencedName, const NameHierarchy& contextName,
const ParseLocation& location)
{
std::string contextNameString = contextName.getQualifiedNameWithSignature();
if (FilePath(contextNameString).exists())
{
contextNameString = FilePath(contextNameString).fileName();
}
m_dump += referenceKindToString(referenceKind) + " " + addLocationSuffix(contextNameString + " -> " + referencedName.getQualifiedNameWithSignature() + " [" + location.filePath.fileName(), location) + "]\n";
}
virtual void onError(const ParseLocation& location, const std::string& message, const std::string& commandline,
bool fatal, bool indexed)
{
m_dump += "ERROR: " + addLocationSuffix(message + " [" + location.filePath.fileName(), location) + "]\n";
}
virtual void onLocalSymbolParsed(const std::string& name, const ParseLocation& location)
{
m_dump += "LOCAL_SYMBOL: " + addLocationSuffix(name + " [" + location.filePath.fileName(), location) + "]\n";
}
virtual void onFileParsed(const FileInfo& fileInfo)
{
m_dump += "FILE: " + fileInfo.path.fileName() + "\n";
}
virtual void onCommentParsed(const ParseLocation& location)
{
m_dump += "COMMENT: " + addLocationSuffix("comment [" + location.filePath.fileName(), location) + "]\n";
}
std::string m_dump;
private:
std::string symbolKindToString(SymbolKind symbolKind) const
{
switch (symbolKind)
{
case SYMBOL_BUILTIN_TYPE:
return "SYMBOL_BUILTIN_TYPE";
case SYMBOL_CLASS:
return "SYMBOL_CLASS";
case SYMBOL_ENUM:
return "SYMBOL_ENUM";
case SYMBOL_ENUM_CONSTANT:
return "SYMBOL_ENUM_CONSTANT";
case SYMBOL_FIELD:
return "SYMBOL_FIELD";
case SYMBOL_FUNCTION:
return "SYMBOL_FUNCTION";
case SYMBOL_GLOBAL_VARIABLE:
return "SYMBOL_GLOBAL_VARIABLE";
case SYMBOL_INTERFACE:
return "SYMBOL_INTERFACE";
case SYMBOL_MACRO:
return "SYMBOL_MACRO";
case SYMBOL_METHOD:
return "SYMBOL_METHOD";
case SYMBOL_NAMESPACE:
return "SYMBOL_NAMESPACE";
case SYMBOL_PACKAGE:
return "SYMBOL_PACKAGE";
case SYMBOL_STRUCT:
return "SYMBOL_STRUCT";
case SYMBOL_TEMPLATE_PARAMETER:
return "SYMBOL_TEMPLATE_PARAMETER";
case SYMBOL_TYPEDEF:
return "SYMBOL_TYPEDEF";
case SYMBOL_TYPE_PARAMETER:
return "SYMBOL_TYPE_PARAMETER";
case SYMBOL_UNION:
return "SYMBOL_UNION";
default:
break;
}
return "SYMBOL_NON_INDEXED";
}
std::string referenceKindToString(ReferenceKind referenceKind) const
{
switch (referenceKind)
{
case REFERENCE_TYPE_USAGE:
return "REFERENCE_TYPE_USAGE";
case REFERENCE_USAGE:
return "REFERENCE_USAGE";
case REFERENCE_CALL:
return "REFERENCE_CALL";
case REFERENCE_INHERITANCE:
return "REFERENCE_INHERITANCE";
case REFERENCE_OVERRIDE:
return "REFERENCE_OVERRIDE";
case REFERENCE_TEMPLATE_ARGUMENT:
return "REFERENCE_TEMPLATE_ARGUMENT";
case REFERENCE_TYPE_ARGUMENT:
return "REFERENCE_TYPE_ARGUMENT";
case REFERENCE_TEMPLATE_DEFAULT_ARGUMENT:
return "REFERENCE_TEMPLATE_DEFAULT_ARGUMENT";
case REFERENCE_TEMPLATE_SPECIALIZATION:
return "REFERENCE_TEMPLATE_SPECIALIZATION";
case REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION:
return "REFERENCE_TEMPLATE_MEMBER_SPECIALIZATION";
case REFERENCE_INCLUDE:
return "REFERENCE_INCLUDE";
case REFERENCE_IMPORT:
return "REFERENCE_IMPORT";
case REFERENCE_MACRO_USAGE:
return "REFERENCE_MACRO_USAGE";
default:
break;
}
return "REFERENCE_UNDEFINED";
}
};
void processSourceFile(const std::string& projectName, const FilePath& sourceFilePath, const bool updateExpectedOutput)
{
const FilePath projectDataRoot = FilePath("data/CxxIndexSampleProjectsTestSuite/" + projectName);
const FilePath projectDataSrcRoot = projectDataRoot.concat(FilePath("src"));
const FilePath projectDataExpectedOutputRoot = projectDataRoot.concat(FilePath("expected_output"));
std::shared_ptr<TextAccess> output = parseCode(projectDataSrcRoot.concat(sourceFilePath), projectDataSrcRoot);
FilePath expectedOutputFilePath = projectDataExpectedOutputRoot.concat(FilePath(utility::replace(sourceFilePath.withoutExtension().str() + ".txt", "/", "_")));
if (updateExpectedOutput || !expectedOutputFilePath.exists())
{
std::ofstream expectedOutputFile;
expectedOutputFile.open(expectedOutputFilePath.str());
expectedOutputFile << output->getText();
expectedOutputFile.close();
}
else
{
std::shared_ptr<TextAccess> expectedOutput = TextAccess::createFromFile(expectedOutputFilePath);
TSM_ASSERT_EQUALS("Output does not match the expected line count for file " + sourceFilePath.str() + " in project " + projectName, expectedOutput->getLineCount(), output->getLineCount());
if (expectedOutput->getLineCount() == output->getLineCount())
{
for (size_t i = 1; i <= expectedOutput->getLineCount(); i++)
{
TS_ASSERT_EQUALS(expectedOutput->getLine(i), output->getLine(i));
}
}
}
}
std::shared_ptr<TextAccess> parseCode(const FilePath& sourceFilePath, const FilePath& projectDataSrcRoot)
{
std::set<FilePath> indexedPaths = { projectDataSrcRoot };
std::set<FilePath> excludedPaths = { };
std::shared_ptr<FileRegister> fileRegister = std::make_shared<FileRegister>(
FileRegisterStateData(),
sourceFilePath,
indexedPaths,
excludedPaths
);
std::shared_ptr<DumpParserClient> parserClient = std::make_shared<DumpParserClient>();
CxxParser parser(parserClient, fileRegister);
std::shared_ptr<IndexerCommandCxxManual> command = std::make_shared<IndexerCommandCxxManual>(
sourceFilePath,
indexedPaths,
excludedPaths,
"c++1z",
utility::concat(std::vector<FilePath> { projectDataSrcRoot }, ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded()),
ApplicationSettings::getInstance()->getFrameworkSearchPathsExpanded(),
std::vector<std::string> { "--target=x86_64-pc-windows-msvc" },
true
);
parser.buildIndex(command);
return TextAccess::createFromString(parserClient->m_dump);
}
};
+84 -1
View File
@@ -2633,7 +2633,7 @@ public:
));
}
void test_cxx_parser_finds_template_member_specialization_of_implicit_template_specialization()
void test_cxx_parser_finds_template_member_specialization_for_method_of_implicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(
"template <typename T>\n"
@@ -2653,6 +2653,89 @@ public:
));
}
void test_cxx_parser_finds_template_member_specialization_for_static_variable_of_implicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(
"template <typename T>\n"
"class A\n"
"{\n"
"public:\n"
" static T foo;\n"
"};\n"
"int main()\n"
"{\n"
" A<int> a;\n"
"}\n"
);
TS_ASSERT(utility::containsElement<std::string>(
client->templateMemberSpecializations, "static int A<int>::foo -> static A<typename T>::T A<typename T>::foo <5:11 5:13>"
));
}
void test_cxx_parser_finds_template_member_specialization_for_field_of_implicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(
"template <typename T>\n"
"class A\n"
"{\n"
"public:\n"
" T foo;\n"
"};\n"
"int main()\n"
"{\n"
" A<int> a;\n"
"}\n"
);
TS_ASSERT(utility::containsElement<std::string>(
client->templateMemberSpecializations, "int A<int>::foo -> A<typename T>::T A<typename T>::foo <5:4 5:6>"
));
}
void test_cxx_parser_finds_template_member_specialization_for_field_of_member_class_of_implicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(
"template <typename T>\n"
"class A\n"
"{\n"
"public:\n"
" class B {\n"
" public:\n"
" T foo;\n"
" };\n"
"};\n"
"int main()\n"
"{\n"
" A<int>::B b;\n"
"}\n"
);
TS_ASSERT(utility::containsElement<std::string>(
client->templateMemberSpecializations, "int A<int>::B::foo -> A<typename T>::T A<typename T>::B::foo <7:5 7:7>"
));
}
void test_cxx_parser_finds_template_member_specialization_for_member_class_of_implicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(
"template <typename T>\n"
"class A\n"
"{\n"
"public:\n"
" class B {};\n"
"};\n"
"int main()\n"
"{\n"
" A<int> a;\n"
"}\n"
);
TS_ASSERT(utility::containsElement<std::string>(
client->templateMemberSpecializations, "A<int>::B -> A<typename T>::B <5:8 5:8>"
));
}
void test_cxx_parser_finds_type_template_argument_of_explicit_template_specialization()
{
std::shared_ptr<TestParserClient> client = parseCode(