#include "data/parser/cxx/name_resolver/CxxDeclNameResolver.h" #include #include "data/parser/cxx/name/CxxFunctionDeclName.h" #include "data/parser/cxx/name/CxxVariableDeclName.h" #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/CxxTypeNameResolver.h" #include "data/parser/cxx/CanonicalFilePathCache.h" #include "data/parser/cxx/utilityClang.h" #include "utility/file/FilePath.h" #include "utility/ScopedSwitcher.h" #include "utility/utilityString.h" CxxDeclNameResolver::CxxDeclNameResolver(std::shared_ptr canonicalFilePathCache) : CxxNameResolver(canonicalFilePathCache, std::vector()) , m_currentDecl(nullptr) { } CxxDeclNameResolver::CxxDeclNameResolver( std::shared_ptr canonicalFilePathCache, std::vector ignoredContextDecls ) : CxxNameResolver(canonicalFilePathCache, ignoredContextDecls) , m_currentDecl(nullptr) { } CxxDeclNameResolver::~CxxDeclNameResolver() { } std::shared_ptr CxxDeclNameResolver::getName(const clang::NamedDecl* declaration) { declaration = utility::getFirstDecl(declaration); std::shared_ptr declName; 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()) { declaration = nullptr; declName = std::make_shared(L"unsolved-lambda", std::vector()); } else { declaration = clang::dyn_cast(declaration)->getLambdaCallOperator(); } } if (declaration) { declName = getDeclName(clang::dyn_cast(declaration)); if (declName) { if (const clang::UsingDecl* usingDecl = clang::dyn_cast_or_null(declaration)) { CxxSpecifierNameResolver specifierNameResolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); declName->setParent(specifierNameResolver.getName(usingDecl->getQualifier())); } else if ( clang::isa(declaration) || clang::isa(declaration) || clang::isa(declaration) ) { clang::ASTContext& context = declaration->getASTContext(); clang::ASTContext::DynTypedNodeList parents = context.getParents(*declaration); for (const clang::ast_type_traits::DynTypedNode* parent = parents.begin(); parent != parents.end(); parent++) { const clang::Decl* parentDecl = parent->get(); while (parentDecl != nullptr) { parentDecl = utility::getFirstDecl(parentDecl); if (clang::isa(parentDecl)) { const clang::TemplateDecl* parentTemplateDecl = clang::dyn_cast_or_null(parentDecl); if (!ignoresContext(parentTemplateDecl) && !ignoresContext(parentTemplateDecl->getTemplatedDecl())) { declName->setParent(getName(parentTemplateDecl)); } break; } else if (clang::isa(parentDecl)) { const clang::ClassTemplatePartialSpecializationDecl* parentClassTemplateDecl = clang::dyn_cast_or_null(parentDecl); if (!ignoresContext(parentDecl)) { declName->setParent(getName(parentClassTemplateDecl)); } break; } else if (clang::isa(parentDecl)) { const clang::VarTemplatePartialSpecializationDecl* parentVarTemplateDecl = clang::dyn_cast_or_null(parentDecl); if (!ignoresContext(parentDecl)) { declName->setParent(getName(parentVarTemplateDecl)); } break; } if (const clang::DeclContext* parentDeclContext = parentDecl->getDeclContext()) { if (ignoresContext(parentDeclContext)) { break; } parentDecl = clang::dyn_cast_or_null(parentDeclContext); if (parentDecl) { if (clang::TemplateDecl* describedTemplate = parentDecl->getDescribedTemplate()) { parentDecl = describedTemplate; } } } else { break; } } } } else { declName->setParent(getContextName(declaration->getDeclContext())); } } } return declName; } std::shared_ptr CxxDeclNameResolver::getContextName(const clang::DeclContext* declContext) { std::shared_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::shared_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::shared_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_shared(getNameForAnonymousSymbol(symbolKindName, declaration), std::vector()); } else if (const clang::CXXRecordDecl* cxxRecordDecl = clang::dyn_cast_or_null(declaration)) { clang::ClassTemplateDecl* templateClassDeclaration = cxxRecordDecl->getDescribedClassTemplate(); if (templateClassDeclaration) { return getDeclName(templateClassDeclaration); } else if (clang::isa(declaration)) { return std::make_shared( std::move(declNameString), getTemplateParameterStringsOfPatrialSpecialitarion( clang::dyn_cast(declaration) ) ); } else if (clang::isa(declaration)) { std::vector templateArguments; const clang::TemplateArgumentList& templateArgumentList = clang::dyn_cast(declaration)->getTemplateArgs(); for (size_t i = 0; i < templateArgumentList.size(); i++) { templateArguments.push_back(getTemplateArgumentName(templateArgumentList.get(i))); } return std::make_shared(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()->getLocStart()); 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 (size_t i = 0; i < templateArgumentList->size(); i++) { const clang::TemplateArgument& templateArgument = templateArgumentList->get(i); 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(getCanonicalFilePathCache(), getIgnoredContextDecls()); typenNameResolver.ignoreContextDecl(functionDecl); std::shared_ptr returnTypeName = CxxTypeName::makeUnsolvedIfNull(typenNameResolver.getName(functionDecl->getReturnType())); std::vector> parameterTypeNames; for (unsigned int i = 0; i < functionDecl->param_size(); i++) { parameterTypeNames.push_back(CxxTypeName::makeUnsolvedIfNull(typenNameResolver.getName(functionDecl->parameters()[i]->getType()))); } if (!clang::isa(declaration) && isStatic) { return std::make_shared( std::move(functionName), std::move(templateArguments), returnTypeName, std::move(parameterTypeNames), getTranslationUnitMainFileName(declaration) ); } return std::make_shared( std::move(functionName), std::move(templateArguments), 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(getCanonicalFilePathCache(), getIgnoredContextDecls()); typenNameResolver.ignoreContextDecl(fieldDecl); std::shared_ptr typeName = CxxTypeName::makeUnsolvedIfNull(typenNameResolver.getName(fieldDecl->getType())); return std::make_shared(std::move(declNameString), std::vector(), typeName, false); } else if (clang::isa(declaration) && clang::dyn_cast(declaration)->isAnonymousNamespace()) { return std::make_shared(getNameForAnonymousSymbol(L"namespace", declaration), std::vector()); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol(L"enum", declaration), std::vector()); } else if ( ( clang::isa(declaration) || clang::isa(declaration) || clang::isa(declaration) ) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol(L"template parameter", declaration), std::vector()); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol(L"parameter", declaration), std::vector()); } 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(getCanonicalFilePathCache(), getIgnoredContextDecls()); typenNameResolver.ignoreContextDecl(varDecl); std::shared_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 = L""; { if (varDecl->getType().isConstQualified()) { scopeFileName = 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 = getTemplateParameterStringsOfPatrialSpecialitarion(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 (size_t i = 0; i < templateArgumentList.size(); i++) { const clang::TemplateArgument& templateArgument = templateArgumentList.get(i); templateParameterNames.push_back(getTemplateArgumentName(templateArgument)); } } return std::make_shared(std::move(varName), std::move(templateParameterNames), 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_shared(std::move(declNameString), getTemplateParameterStrings(clang::dyn_cast(declaration))); } if (!declNameString.empty()) { return std::make_shared(std::move(declNameString), std::vector(), std::shared_ptr()); } } // LOG_ERROR("could not resolve name of decl at: " + declaration->getLocation().printToString(sourceManager)); return std::make_shared(getNameForAnonymousSymbol(L"symbol", declaration), std::vector()); } std::wstring CxxDeclNameResolver::getTranslationUnitMainFileName(const clang::Decl* declaration) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); clang::FileID fileId = sourceManager.getMainFileID(); if (fileId.isValid()) { const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(fileId); return getCanonicalFilePathCache()->getCanonicalFilePath(fileEntry).fileName(); } return L""; } std::wstring CxxDeclNameResolver::getDeclarationFileName(const clang::Decl* declaration) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); const clang::FileEntry* fileEntry = sourceManager.getFileEntryForID(sourceManager.getFileID(declaration->getLocStart())); if (fileEntry != nullptr && fileEntry->isValid()) { return getCanonicalFilePathCache()->getCanonicalFilePath(fileEntry).fileName(); } return getCanonicalFilePathCache()->getCanonicalFilePath(utility::decodeFromUtf8(sourceManager.getPresumedLoc(declaration->getLocStart()).getFilename())).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->getLocStart()); if (presumedBegin.isValid()) { return L"anonymous " + symbolKindName + L" (" + 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 (size_t i = 0; i < parameterList->size(); i++) { templateParameterStrings.push_back(getTemplateParameterString(parameterList->getParam(i))); } return templateParameterStrings; } std::wstring CxxDeclNameResolver::getTemplateParameterString(const clang::NamedDecl* parameter) { std::wstring templateParameterTypeString; if (parameter) { 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()); } else // works for partial template specializations { typeNameResolver.ignoreContextDecl(m_currentDecl); } 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; } std::wstring CxxDeclNameResolver::getTemplateArgumentName(const clang::TemplateArgument& argument) { CxxTemplateArgumentNameResolver resolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); return resolver.getTemplateArgumentName(argument); }