#include "CxxDeclNameResolver.h" #include #include "CanonicalFilePathCache.h" #include "CxxFunctionDeclName.h" #include "CxxSpecifierNameResolver.h" #include "CxxStaticFunctionDeclName.h" #include "CxxTemplateArgumentNameResolver.h" #include "CxxTemplateParameterStringResolver.h" #include "CxxTypeNameResolver.h" #include "CxxVariableDeclName.h" #include "ScopedSwitcher.h" #include "utilityClang.h" #include "utilityString.h" CxxDeclNameResolver::CxxDeclNameResolver(CanonicalFilePathCache* canonicalFilePathCache) : CxxNameResolver(canonicalFilePathCache), m_currentDecl(nullptr) { } CxxDeclNameResolver::CxxDeclNameResolver(const CxxNameResolver* other) : CxxNameResolver(other), m_currentDecl(nullptr) { } std::unique_ptr CxxDeclNameResolver::getName(const clang::NamedDecl* declaration) { declaration = utility::getFirstDecl(declaration); if ((declaration) && (clang::isa(declaration)) && (clang::dyn_cast(declaration)->isLambda())) { // avoid triggering assert clang::DeclarationName Name = clang::dyn_cast(declaration) ->getASTContext() .DeclarationNames.getCXXOperatorName(clang::OO_Call); clang::DeclContext::lookup_result Calls = clang::dyn_cast(declaration)->lookup(Name); if (Calls.empty()) { return std::make_unique(L"unsolved-lambda"); } else { declaration = clang::dyn_cast(declaration)->getLambdaCallOperator(); } } if (!declaration) { return nullptr; } std::unique_ptr declName = getDeclName( clang::dyn_cast(declaration)); if (declName) { if (const clang::UsingDecl* usingDecl = clang::dyn_cast_or_null(declaration)) { declName->setParent(CxxSpecifierNameResolver(this).getName(usingDecl->getQualifier())); } else if ( !clang::isa(declaration) && !clang::isa(declaration) && !clang::isa(declaration)) { declName->setParent(getContextName(declaration->getDeclContext())); } } return declName; } std::unique_ptr CxxDeclNameResolver::getContextName(const clang::DeclContext* declContext) { std::unique_ptr contextDeclName; if (declContext && !ignoresContext(declContext)) { if (const clang::NamedDecl* contextNamedDecl = clang::dyn_cast_or_null( declContext)) { contextDeclName = getDeclName(contextNamedDecl); if (contextDeclName) { contextDeclName->setParent(getContextName(declContext->getParent())); } else { contextDeclName = getContextName(declContext->getParent()); } } } return contextDeclName; } std::unique_ptr CxxDeclNameResolver::getDeclName(const clang::NamedDecl* declaration) { if (declaration) { ScopedSwitcher switcher(m_currentDecl, declaration); std::wstring declNameString = utility::decodeFromUtf8(declaration->getNameAsString()); if (const clang::TagDecl* tagDecl = clang::dyn_cast_or_null(declaration)) { if (const clang::TypedefNameDecl* typedefNameDecl = tagDecl->getTypedefNameForAnonDecl()) { declNameString = utility::decodeFromUtf8(typedefNameDecl->getNameAsString()); } } if (const clang::TypeAliasDecl* typeAliasDecl = clang::dyn_cast_or_null(declaration)) { clang::TypeAliasTemplateDecl* templatedDeclaration = typeAliasDecl->getDescribedAliasTemplate(); if (templatedDeclaration) { return getDeclName(templatedDeclaration); } } if (const clang::RecordDecl* recordDecl = clang::dyn_cast_or_null( declaration)) { if (recordDecl->isLambda()) { // we skip this node because its child (the lambda call operator) has already been recorded. return std::unique_ptr(); } else if (declNameString.empty()) { std::wstring symbolKindName = L"class"; if (recordDecl->isStruct()) { symbolKindName = L"struct"; } else if (recordDecl->isUnion()) { symbolKindName = L"union"; } return std::make_unique( getNameForAnonymousSymbol(symbolKindName, declaration)); } else if (const clang::CXXRecordDecl* cxxRecordDecl = clang::dyn_cast_or_null(declaration)) { if (clang::ClassTemplateDecl* templateClassDeclaration = cxxRecordDecl->getDescribedClassTemplate()) { return getDeclName(templateClassDeclaration); } else if (clang::isa(declaration)) { return std::make_unique( std::move(declNameString), getTemplateParameterStringsOfPartialSpecialization( clang::dyn_cast( declaration))); } else if ( const clang::ClassTemplateSpecializationDecl* templateSpecialitarionDecl = clang::dyn_cast_or_null(declaration)) { std::vector templateArguments; const clang::TemplateArgumentList& templateArgumentList = templateSpecialitarionDecl->getTemplateArgs(); for (unsigned i = 0; i < templateArgumentList.size(); i++) { if (templateArgumentList.get(i).isDependent()) { llvm::PointerUnion pu = templateSpecialitarionDecl->getSpecializedTemplateOrPartial(); if (pu.is()) { return getDeclName(pu.get()); } else if (pu.is()) { return getDeclName( pu.get()); } } templateArguments.push_back( getTemplateArgumentName(templateArgumentList.get(i))); } return std::make_unique( std::move(declNameString), std::move(templateArguments)); } } } else if (clang::isa(declaration)) { const clang::FunctionDecl* functionDecl = clang::dyn_cast( declaration); std::wstring functionName = declNameString; std::vector templateArguments; if ((clang::dyn_cast_or_null(functionDecl)) && (clang::dyn_cast_or_null(functionDecl)->getParent()->isLambda())) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc( clang::dyn_cast_or_null(functionDecl) ->getParent() ->getBeginLoc()); functionName = L"lambda at " + std::to_wstring(presumedBegin.getLine()) + L":" + std::to_wstring(presumedBegin.getColumn()); } else if (clang::FunctionTemplateDecl* templateFunctionDeclaration = functionDecl->getDescribedFunctionTemplate()) { templateArguments = getTemplateParameterStrings(templateFunctionDeclaration); } else if (functionDecl->isFunctionTemplateSpecialization()) { const clang::TemplateArgumentList* templateArgumentList = functionDecl->getTemplateSpecializationArgs(); for (unsigned i = 0; i < templateArgumentList->size(); i++) { const clang::TemplateArgument& templateArgument = templateArgumentList->get(i); if (templateArgument.isDependent()) { if (clang::FunctionTemplateDecl* templateFunctionDeclaration = functionDecl->getPrimaryTemplate()) { return getDeclName(templateFunctionDeclaration); } } templateArguments.push_back(getTemplateArgumentName(templateArgument)); } } bool isStatic = false; bool isConst = false; if (clang::isa(declaration)) { const clang::CXXMethodDecl* methodDecl = clang::dyn_cast( declaration); isStatic = methodDecl->isStatic(); isConst = methodDecl->isConst(); } else { isStatic = functionDecl->getStorageClass() == clang::SC_Static; } CxxTypeNameResolver typenNameResolver(this); typenNameResolver.ignoreContextDecl(functionDecl); std::unique_ptr returnTypeName = CxxTypeName::makeUnsolvedIfNull( typenNameResolver.getName(functionDecl->getReturnType())); std::vector> parameterTypeNames; for (unsigned int i = 0; i < functionDecl->param_size(); i++) { if (const clang::SubstTemplateTypeParmType* substType = clang::dyn_cast_or_null( functionDecl->parameters()[i]->getType().getTypePtr())) { if (const clang::TemplateTypeParmType* templateParamType = substType->getReplacedParameter()) { if (templateParamType->isParameterPack()) { parameterTypeNames.push_back(std::make_unique(L"...")); break; } } } parameterTypeNames.push_back(CxxTypeName::makeUnsolvedIfNull( typenNameResolver.getName(functionDecl->parameters()[i]->getType()))); } if (!clang::isa(declaration) && isStatic) { return std::make_unique( std::move(functionName), std::move(templateArguments), std::move(returnTypeName), std::move(parameterTypeNames), getTranslationUnitMainFileName(declaration)); } return std::make_unique( std::move(functionName), std::move(templateArguments), std::move(returnTypeName), std::move(parameterTypeNames), isConst, isStatic); } else if (clang::isa(declaration)) { const clang::FunctionTemplateDecl* functionTemplateDecl = clang::dyn_cast(declaration); return getDeclName(functionTemplateDecl->getTemplatedDecl()); } else if (clang::isa(declaration)) { const clang::FieldDecl* fieldDecl = clang::dyn_cast(declaration); CxxTypeNameResolver typenNameResolver(this); typenNameResolver.ignoreContextDecl(fieldDecl); std::unique_ptr typeName = CxxTypeName::makeUnsolvedIfNull( typenNameResolver.getName(fieldDecl->getType())); return std::make_unique( std::move(declNameString), std::vector(), std::move(typeName), false); } else if ( clang::isa(declaration) && clang::dyn_cast(declaration)->isAnonymousNamespace()) { declaration = clang::dyn_cast(declaration)->getOriginalNamespace(); return std::make_unique(getNameForAnonymousSymbol(L"namespace", declaration)); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_unique(getNameForAnonymousSymbol(L"enum", declaration)); } else if ( (clang::isa(declaration) || clang::isa(declaration) || clang::isa(declaration)) && declNameString.empty()) { return std::make_unique( getNameForAnonymousSymbol(L"template parameter", declaration)); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_unique(getNameForAnonymousSymbol(L"parameter", declaration)); } else if (clang::isa(declaration)) { const clang::VarDecl* varDecl = clang::dyn_cast(declaration); if (varDecl->getParentFunctionOrMethod() == nullptr) { bool isStatic = false; if (varDecl->getAccess() != clang::AS_none) { // var is declared inside a type and must be static (non-statics are stored as // clang::FieldDecl) isStatic = true; } else { // nothing todo, varDecl is global (and non-static) } CxxTypeNameResolver typenNameResolver(this); typenNameResolver.ignoreContextDecl(varDecl); std::unique_ptr typeName = CxxTypeName::makeUnsolvedIfNull( typenNameResolver.getName(varDecl->getType())); std::wstring varName = declNameString; if (utility::getSymbolKind(varDecl) == SYMBOL_GLOBAL_VARIABLE && varDecl->getStorageClass() == clang::SC_Static) { // if a global variable is static it is only visible in the current translation // unit. Therefore if multiple instances of that global variable may be // generated (one for each translation unit) we add the name of the translation // unit's source file. If that global variable definition is const, we add the // name of the (maybe header) file that variable is defined in instead. This // causes different instances of the variable that all MUST contain the same // value to be merged into a single node in Sourcetrail. std::wstring scopeFileName; if (varDecl->getType().isConstQualified()) { scopeFileName = getCanonicalFilePathCache()->getDeclarationFileName( declaration); } else { scopeFileName = getTranslationUnitMainFileName(declaration); } if (!scopeFileName.empty()) { varName = declNameString + L" (" + scopeFileName + L')'; } } std::vector templateParameterNames; if (varDecl->getDescribedVarTemplate()) { const clang::VarTemplateDecl* templateDeclaration = varDecl->getDescribedVarTemplate(); templateParameterNames = getTemplateParameterStrings(templateDeclaration); } else if (clang::isa(declaration)) { templateParameterNames = getTemplateParameterStringsOfPartialSpecialization( clang::dyn_cast(declaration)); } else if (clang::isa(declaration)) { const clang::VarTemplateSpecializationDecl* templateSpecializationDeclaration = clang::dyn_cast_or_null(varDecl); const clang::TemplateArgumentList& templateArgumentList = templateSpecializationDeclaration->getTemplateArgs(); for (unsigned i = 0; i < templateArgumentList.size(); i++) { const clang::TemplateArgument& templateArgument = templateArgumentList.get(i); if (templateArgument.isDependent()) { llvm::PointerUnion pu = templateSpecializationDeclaration->getSpecializedTemplateOrPartial(); if (pu.is()) { return getDeclName(pu.get()); } else if (pu.is()) { return getDeclName( pu.get()); } } templateParameterNames.push_back(getTemplateArgumentName(templateArgument)); } } return std::make_unique( std::move(varName), std::move(templateParameterNames), std::move(typeName), isStatic); } } else if (clang::isa(declaration)) { const clang::VarTemplateDecl* varTemplateDecl = clang::dyn_cast( declaration); return getDeclName(varTemplateDecl->getTemplatedDecl()); } else if (clang::isa( declaration)) // also triggers on TemplateTemplateParmDecl { return std::make_unique( std::move(declNameString), getTemplateParameterStrings(clang::dyn_cast(declaration))); } if (!declNameString.empty()) { return std::make_unique(std::move(declNameString)); } } // LOG_ERROR("could not resolve name of decl at: " + // declaration->getLocation().printToString(sourceManager)); return std::make_unique(getNameForAnonymousSymbol(L"symbol", declaration)); } std::wstring CxxDeclNameResolver::getTranslationUnitMainFileName(const clang::Decl* declaration) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); return getCanonicalFilePathCache() ->getCanonicalFilePath(sourceManager.getMainFileID(), sourceManager) .fileName(); } std::wstring CxxDeclNameResolver::getNameForAnonymousSymbol( const std::wstring& symbolKindName, const clang::Decl* declaration) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(declaration->getBeginLoc()); if (presumedBegin.isValid()) { return L"anonymous " + symbolKindName + L" (" + getCanonicalFilePathCache()->getDeclarationFileName(declaration) + L'<' + std::to_wstring(presumedBegin.getLine()) + L':' + std::to_wstring(presumedBegin.getColumn()) + L">)"; } return L"anonymous " + symbolKindName; } std::vector CxxDeclNameResolver::getTemplateParameterStrings( const clang::TemplateDecl* templateDecl) { std::vector templateParameterStrings; clang::TemplateParameterList* parameterList = templateDecl->getTemplateParameters(); for (unsigned i = 0; i < parameterList->size(); i++) { templateParameterStrings.push_back(getTemplateParameterString(parameterList->getParam(i))); } return templateParameterStrings; } std::wstring CxxDeclNameResolver::getTemplateParameterString(const clang::NamedDecl* parameter) { CxxTemplateParameterStringResolver parameterStringResolver(this); if (clang::isa(m_currentDecl) && clang::dyn_cast(m_currentDecl)->getTemplatedDecl()) { parameterStringResolver.ignoreContextDecl( clang::dyn_cast(m_currentDecl)->getTemplatedDecl()); } else // works for partial template specializations { parameterStringResolver.ignoreContextDecl(m_currentDecl); } parameterStringResolver.ignoreContextDecl(parameter); return parameterStringResolver.getTemplateParameterString(parameter); } std::wstring CxxDeclNameResolver::getTemplateArgumentName(const clang::TemplateArgument& argument) { return CxxTemplateArgumentNameResolver(this).getTemplateArgumentName(argument); }