From 4c92d51b633df4754e6b1dcebd2a638cf2742aac Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 20 Mar 2018 18:47:42 +0100 Subject: [PATCH] logic: fixed infinite recursion while solving c++ template parameter names (issue #553) --- src/lib_cxx/CMakeLists.txt | 2 + .../cxx/name_resolver/CxxDeclNameResolver.cpp | 90 ++------------- .../cxx/name_resolver/CxxDeclNameResolver.h | 4 - .../cxx/name_resolver/CxxNameResolver.cpp | 4 - .../cxx/name_resolver/CxxNameResolver.h | 2 +- .../CxxSpecifierNameResolver.cpp | 4 - .../name_resolver/CxxSpecifierNameResolver.h | 1 - .../CxxTemplateArgumentNameResolver.cpp | 4 - .../CxxTemplateArgumentNameResolver.h | 1 - .../CxxTemplateParameterStringResolver.cpp | 103 ++++++++++++++++++ .../CxxTemplateParameterStringResolver.h | 25 +++++ .../cxx/name_resolver/CxxTypeNameResolver.cpp | 4 - .../cxx/name_resolver/CxxTypeNameResolver.h | 1 - src/test/CxxParserTestSuite.h | 14 +++ 14 files changed, 154 insertions(+), 105 deletions(-) create mode 100644 src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.cpp create mode 100644 src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h diff --git a/src/lib_cxx/CMakeLists.txt b/src/lib_cxx/CMakeLists.txt index abcacc03..9c24dd7e 100644 --- a/src/lib_cxx/CMakeLists.txt +++ b/src/lib_cxx/CMakeLists.txt @@ -37,6 +37,8 @@ add_files( data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.cpp data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h + data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.cpp + data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h data/parser/cxx/name_resolver/CxxTypeNameResolver.cpp data/parser/cxx/name_resolver/CxxTypeNameResolver.h diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.cpp index 01e293d7..383c3cca 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.cpp +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.cpp @@ -7,6 +7,7 @@ #include "data/parser/cxx/name/CxxStaticFunctionDeclName.h" #include "data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h" #include "data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h" +#include "data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h" #include "data/parser/cxx/name_resolver/CxxTypeNameResolver.h" #include "data/parser/cxx/CanonicalFilePathCache.h" #include "data/parser/cxx/utilityClang.h" @@ -29,10 +30,6 @@ CxxDeclNameResolver::CxxDeclNameResolver( { } -CxxDeclNameResolver::~CxxDeclNameResolver() -{ -} - std::shared_ptr CxxDeclNameResolver::getName(const clang::NamedDecl* declaration) { declaration = utility::getFirstDecl(declaration); @@ -480,89 +477,18 @@ std::vector CxxDeclNameResolver::getTemplateParameterStrings(const std::wstring CxxDeclNameResolver::getTemplateParameterString(const clang::NamedDecl* parameter) { - std::wstring templateParameterTypeString; - - if (parameter) + CxxTemplateParameterStringResolver parameterStringResolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); + if (clang::isa(m_currentDecl) && clang::dyn_cast(m_currentDecl)->getTemplatedDecl()) { - const clang::Decl::Kind templateParameterKind = parameter->getKind(); - switch (templateParameterKind) - { - case clang::Decl::NonTypeTemplateParm: - templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); - break; - case clang::Decl::TemplateTypeParm: - templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); - break; - case clang::Decl::TemplateTemplateParm: - templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); - break; - default: - // LOG_ERROR("Unhandled kind of template parameter."); - break; - } - - std::wstring parameterName = utility::decodeFromUtf8(parameter->getName()); - if (!parameterName.empty()) - { - templateParameterTypeString += L" " + parameterName; - } - } - return templateParameterTypeString; -} - -std::wstring CxxDeclNameResolver::getTemplateParameterTypeString(const clang::NonTypeTemplateParmDecl* parameter) -{ - CxxTypeNameResolver typeNameResolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); - - if (clang::isa(m_currentDecl)) - { - typeNameResolver.ignoreContextDecl(clang::dyn_cast(m_currentDecl)->getTemplatedDecl()); + parameterStringResolver.ignoreContextDecl(clang::dyn_cast(m_currentDecl)->getTemplatedDecl()); } else // works for partial template specializations { - typeNameResolver.ignoreContextDecl(m_currentDecl); + parameterStringResolver.ignoreContextDecl(m_currentDecl); } + parameterStringResolver.ignoreContextDecl(parameter); - std::wstring typeString; - - std::shared_ptr typeName = CxxTypeName::makeUnsolvedIfNull(typeNameResolver.getName(parameter->getType())); - typeString = typeName->toString(); - - if (parameter->isTemplateParameterPack()) - { - typeString += L"..."; - } - return typeString; -} - -std::wstring CxxDeclNameResolver::getTemplateParameterTypeString(const clang::TemplateTypeParmDecl* parameter) -{ - std::wstring typeString = (parameter->wasDeclaredWithTypename() ? L"typename" : L"class"); - if (parameter->isTemplateParameterPack()) - { - typeString += L"..."; - } - return typeString; -} - -std::wstring CxxDeclNameResolver::getTemplateParameterTypeString(const clang::TemplateTemplateParmDecl* parameter) -{ - std::wstring templateParameterTypeString = L"template<"; - clang::TemplateParameterList* parameterList = parameter->getTemplateParameters(); - for (size_t i = 0; i < parameterList->size(); i++) - { - templateParameterTypeString += getTemplateParameterString(parameterList->getParam(i)); - templateParameterTypeString += (i < parameterList->size() - 1) ? L", " : L""; - } - templateParameterTypeString += L">"; - templateParameterTypeString += L" typename"; // TODO: what if template template parameter is defined with class keyword? - - if (parameter->isTemplateParameterPack()) - { - templateParameterTypeString += L"..."; - } - - return templateParameterTypeString; + return parameterStringResolver.getTemplateParameterString(parameter); } std::wstring CxxDeclNameResolver::getTemplateArgumentName(const clang::TemplateArgument& argument) @@ -570,3 +496,5 @@ std::wstring CxxDeclNameResolver::getTemplateArgumentName(const clang::TemplateA CxxTemplateArgumentNameResolver resolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); return resolver.getTemplateArgumentName(argument); } + + diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.h index c1bcd1d7..682f8686 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.h +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxDeclNameResolver.h @@ -16,7 +16,6 @@ public: std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ); - virtual ~CxxDeclNameResolver(); std::shared_ptr getName(const clang::NamedDecl* declaration); @@ -30,9 +29,6 @@ private: template std::vector getTemplateParameterStringsOfPatrialSpecialitarion(const T* templateDecl); std::wstring getTemplateParameterString(const clang::NamedDecl* parameter); - std::wstring getTemplateParameterTypeString(const clang::NonTypeTemplateParmDecl* parameter); - std::wstring getTemplateParameterTypeString(const clang::TemplateTypeParmDecl* parameter); - std::wstring getTemplateParameterTypeString(const clang::TemplateTemplateParmDecl* parameter); std::wstring getTemplateArgumentName(const clang::TemplateArgument& argument); const clang::NamedDecl* m_currentDecl; diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.cpp index 8b3109d8..c8ed8eb0 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.cpp +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.cpp @@ -9,10 +9,6 @@ CxxNameResolver::CxxNameResolver( { } -CxxNameResolver::~CxxNameResolver() -{ -} - void CxxNameResolver::ignoreContextDecl(const clang::Decl* decl) { if (decl) diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.h index 91ef7d92..9d34f51e 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.h +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxNameResolver.h @@ -14,7 +14,7 @@ public: std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ); - virtual ~CxxNameResolver(); + virtual ~CxxNameResolver() = default; void ignoreContextDecl(const clang::Decl* decl); bool ignoresContext(const clang::Decl* decl); diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.cpp index a023a647..f29f3dfb 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.cpp +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.cpp @@ -21,10 +21,6 @@ CxxSpecifierNameResolver::CxxSpecifierNameResolver( { } -CxxSpecifierNameResolver::~CxxSpecifierNameResolver() -{ -} - std::shared_ptr CxxSpecifierNameResolver::getName(const clang::NestedNameSpecifier* nestedNameSpecifier) { std::shared_ptr name; diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h index f0dc34f7..720ab306 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxSpecifierNameResolver.h @@ -15,7 +15,6 @@ public: std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ); - virtual ~CxxSpecifierNameResolver(); std::shared_ptr getName(const clang::NestedNameSpecifier* nestedNameSpecifier); }; diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.cpp index c683a2a0..499600a1 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.cpp +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.cpp @@ -19,10 +19,6 @@ CxxTemplateArgumentNameResolver::CxxTemplateArgumentNameResolver( { } -CxxTemplateArgumentNameResolver::~CxxTemplateArgumentNameResolver() -{ -} - std::wstring CxxTemplateArgumentNameResolver::getTemplateArgumentName(const clang::TemplateArgument& argument) { // This doesn't work correctly if the template argument is dependent. diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h index edd72086..503e0d70 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateArgumentNameResolver.h @@ -15,7 +15,6 @@ public: std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ); - virtual ~CxxTemplateArgumentNameResolver(); std::wstring getTemplateArgumentName(const clang::TemplateArgument& argument); }; diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.cpp new file mode 100644 index 00000000..17830db2 --- /dev/null +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.cpp @@ -0,0 +1,103 @@ +#include "data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h" + +#include +#include + +#include "data/parser/cxx/name_resolver/CxxTypeNameResolver.h" +#include "utility/utilityString.h" + +CxxTemplateParameterStringResolver::CxxTemplateParameterStringResolver(std::shared_ptr canonicalFilePathCache) + : CxxNameResolver(canonicalFilePathCache, std::vector()) +{ +} + +CxxTemplateParameterStringResolver::CxxTemplateParameterStringResolver( + std::shared_ptr canonicalFilePathCache, + std::vector ignoredContextDecls +) + : CxxNameResolver(canonicalFilePathCache, ignoredContextDecls) +{ +} + +std::wstring CxxTemplateParameterStringResolver::getTemplateParameterString(const clang::NamedDecl* parameter) +{ + std::wstring templateParameterTypeString; + + if (parameter) + { + const std::wstring parameterName = utility::decodeFromUtf8(parameter->getName()); + + const clang::Decl::Kind templateParameterKind = parameter->getKind(); + switch (templateParameterKind) + { + case clang::Decl::NonTypeTemplateParm: + templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); + break; + case clang::Decl::TemplateTypeParm: + templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); + break; + case clang::Decl::TemplateTemplateParm: + templateParameterTypeString = getTemplateParameterTypeString(clang::dyn_cast(parameter)); + break; + default: + // LOG_ERROR("Unhandled kind of template parameter."); + break; + } + + if (!parameterName.empty()) + { + templateParameterTypeString += L" " + parameterName; + } + } + return templateParameterTypeString; +} + +std::wstring CxxTemplateParameterStringResolver::getTemplateParameterTypeString(const clang::NonTypeTemplateParmDecl* parameter) +{ + CxxTypeNameResolver typeNameResolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); + std::shared_ptr typeName = CxxTypeName::makeUnsolvedIfNull(typeNameResolver.getName(parameter->getType())); + + std::wstring typeString = typeName->toString(); + + if (parameter->isTemplateParameterPack()) + { + typeString += L"..."; + } + + return typeString; +} + +std::wstring CxxTemplateParameterStringResolver::getTemplateParameterTypeString(const clang::TemplateTypeParmDecl* parameter) +{ + std::wstring typeString = (parameter->wasDeclaredWithTypename() ? L"typename" : L"class"); + + if (parameter->isTemplateParameterPack()) + { + typeString += L"..."; + } + + return typeString; +} + +std::wstring CxxTemplateParameterStringResolver::getTemplateParameterTypeString(const clang::TemplateTemplateParmDecl* parameter) +{ + std::wstring templateParameterTypeString = L"template<"; + clang::TemplateParameterList* parameterList = parameter->getTemplateParameters(); + for (size_t i = 0; i < parameterList->size(); i++) + { + CxxTemplateParameterStringResolver parameterStringResolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); + parameterStringResolver.ignoreContextDecl(parameterList->getParam(i)); + + templateParameterTypeString += parameterStringResolver.getTemplateParameterString(parameterList->getParam(i)); + templateParameterTypeString += (i < parameterList->size() - 1) ? L", " : L""; + } + templateParameterTypeString += L">"; + templateParameterTypeString += L" typename"; // TODO: what if template template parameter is defined with class keyword? + + if (parameter->isTemplateParameterPack()) + { + templateParameterTypeString += L"..."; + } + + return templateParameterTypeString; +} diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h new file mode 100644 index 00000000..60ab3b12 --- /dev/null +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTemplateParameterStringResolver.h @@ -0,0 +1,25 @@ +#ifndef CXX_TEMPLATE_PARAMETER_STRING_RESOLVER_H +#define CXX_TEMPLATE_PARAMETER_STRING_RESOLVER_H + +#include + +#include "data/parser/cxx/name_resolver/CxxNameResolver.h" + +class DataType; + +class CxxTemplateParameterStringResolver: public CxxNameResolver +{ +public: + CxxTemplateParameterStringResolver(std::shared_ptr canonicalFilePathCache); + CxxTemplateParameterStringResolver( + std::shared_ptr canonicalFilePathCache, + std::vector ignoredContextDecls + ); + + std::wstring getTemplateParameterString(const clang::NamedDecl* parameter); + std::wstring getTemplateParameterTypeString(const clang::NonTypeTemplateParmDecl* parameter); + std::wstring getTemplateParameterTypeString(const clang::TemplateTypeParmDecl* parameter); + std::wstring getTemplateParameterTypeString(const clang::TemplateTemplateParmDecl* parameter); +}; + +#endif // CXX_TEMPLATE_PARAMETER_STRING_RESOLVER_H diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.cpp b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.cpp index 4b597b7b..f037eca4 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.cpp +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.cpp @@ -23,10 +23,6 @@ CxxTypeNameResolver::CxxTypeNameResolver( { } -CxxTypeNameResolver::~CxxTypeNameResolver() -{ -} - std::shared_ptr CxxTypeNameResolver::getName(const clang::QualType& qualType) { std::shared_ptr typeName = getName(qualType.getTypePtr()); diff --git a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.h b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.h index 32b79773..7fd10b37 100644 --- a/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.h +++ b/src/lib_cxx/data/parser/cxx/name_resolver/CxxTypeNameResolver.h @@ -12,7 +12,6 @@ public: std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ); - virtual ~CxxTypeNameResolver(); std::shared_ptr getName(const clang::QualType& qualType); std::shared_ptr getName(const clang::Type* type); diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index b2f3f281..08959685 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -860,6 +860,20 @@ public: )); } + void test_cxx_parser_finds_non_type_template_parameter_that_depends_on_type_template_parameter_of_template_template_parameter() + { + std::shared_ptr client = parseCode( + "template typename S>\n" + "class A\n" + "{\n" + "};\n" + ); + + TS_ASSERT(utility::containsElement( + client->templateParameterTypes, L"A typename S>::S <1:45 1:45>" + )); + } + void test_cxx_parser_finds_template_argument_of_dependent_non_type_template_parameter() { std::shared_ptr client = parseCode(