#include "data/parser/cxx/name_resolver/CxxDeclNameResolver.h" #include #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("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 (const clang::TemplateDecl* parentTemplateDecl = clang::dyn_cast_or_null(parentDecl)) { if (!ignoresContext(parentTemplateDecl) && !ignoresContext(parentTemplateDecl->getTemplatedDecl())) { declName->setParent(getName(parentTemplateDecl)); } break; } else if (const clang::ClassTemplatePartialSpecializationDecl* parentTemplateDecl = clang::dyn_cast_or_null(parentDecl)) { if (!ignoresContext(parentDecl)) { declName->setParent(getName(parentTemplateDecl)); } 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::string declNameString = declaration->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::string symbolKindName = "class"; if (recordDecl->isStruct()) { symbolKindName = "struct"; } else if (recordDecl->isUnion()) { symbolKindName = "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)) { const clang::ClassTemplatePartialSpecializationDecl* partialSpecializationDecl = clang::dyn_cast(declaration); clang::TemplateParameterList* parameterList = partialSpecializationDecl->getTemplateParameters(); unsigned int currentParameterIndex = 0; std::vector templateParameters; const clang::TemplateArgumentList& templateArgumentList = partialSpecializationDecl->getTemplateArgs(); const int templateArgumentCount = templateArgumentList.size(); for (int i = 0; i < templateArgumentCount; i++) { const clang::TemplateArgument& templateArgument = templateArgumentList.get(i); if (templateArgument.isDependent()) // IMPORTANT_TODO: fix case when arg depends on template parameter of outer template class, or depends on first template parameter. { if(currentParameterIndex < parameterList->size()) { templateParameters.push_back(getTemplateParameterString(parameterList->getParam(currentParameterIndex))); } else { //this if fixes the crash, but not the problem TODO // const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); // LOG_ERROR("Template getParam out of Range " + declaration->getLocation().printToString(sourceManager)); } currentParameterIndex++; } else { templateParameters.push_back(getTemplateArgumentName(templateArgument)); } } return std::make_shared(declNameString, templateParameters); } 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(declNameString, templateArguments); } } } else if (clang::isa(declaration)) { const clang::FunctionDecl* functionDecl = clang::dyn_cast(declaration); std::string 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 = "lambda at " + std::to_string(presumedBegin.getLine()) + ":" + std::to_string(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( functionName, templateArguments, returnTypeName, parameterTypeNames, getTranslationUnitMainFileName(declaration) ); } return std::make_shared( functionName, templateArguments, returnTypeName, 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)) // also triggers on TemplateTemplateParmDecl { return std::make_shared(declNameString, getTemplateParameterStrings(clang::dyn_cast(declaration))); } 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(declNameString, std::vector(), typeName, false); } else if (clang::isa(declaration) && clang::dyn_cast(declaration)->isAnonymousNamespace()) { return std::make_shared(getNameForAnonymousSymbol("namespace", declaration), std::vector()); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol("enum", declaration), std::vector()); } else if ( ( clang::isa(declaration) || clang::isa(declaration) || clang::isa(declaration) ) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol("template parameter", declaration), std::vector()); } else if (clang::isa(declaration) && declNameString.empty()) { return std::make_shared(getNameForAnonymousSymbol("parameter", declaration), std::vector()); } else if (clang::isa(declaration)) { const clang::VarDecl* varDecl = clang::dyn_cast(declaration); if (varDecl->getParentFunctionOrMethod() == NULL) { 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::string 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::string scopeFileName = ""; { if (varDecl->getType().isConstQualified()) { scopeFileName = getDeclarationFileName(declaration); } else { scopeFileName = getTranslationUnitMainFileName(declaration); } } if (!scopeFileName.empty()) { varName = declNameString + " (" + scopeFileName + ")"; } } return std::make_shared(varName, std::vector(), typeName, isStatic); } } if (!declNameString.empty()) { return std::make_shared(declNameString, std::vector(), std::shared_ptr()); } } // LOG_ERROR("could not resolve name of decl at: " + declaration->getLocation().printToString(sourceManager)); return std::make_shared(getNameForAnonymousSymbol("symbol", declaration), std::vector()); } std::string 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 ""; } std::string 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(sourceManager.getPresumedLoc(declaration->getLocStart()).getFilename()).fileName(); } std::string CxxDeclNameResolver::getNameForAnonymousSymbol(const std::string& symbolKindName, const clang::Decl* declaration) { const clang::SourceManager& sourceManager = declaration->getASTContext().getSourceManager(); const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(declaration->getLocStart()); if (presumedBegin.isValid()) { return "anonymous " + symbolKindName + " (" + getDeclarationFileName(declaration) + "<" + std::to_string(presumedBegin.getLine()) + ":" + std::to_string(presumedBegin.getColumn()) + ">)"; } return "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::string CxxDeclNameResolver::getTemplateParameterString(const clang::NamedDecl* parameter) { std::string templateParameterTypeString; if (parameter) { 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::string parameterName = parameter->getName(); if (!parameterName.empty()) { templateParameterTypeString += " " + parameterName; } } return templateParameterTypeString; } std::string 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::string typeString; std::shared_ptr typeName = CxxTypeName::makeUnsolvedIfNull(typeNameResolver.getName(parameter->getType())); typeString = typeName->toString(); if (parameter->isTemplateParameterPack()) { typeString += "..."; } return typeString; } std::string CxxDeclNameResolver::getTemplateParameterTypeString(const clang::TemplateTypeParmDecl* parameter) { std::string typeString = (parameter->wasDeclaredWithTypename() ? "typename" : "class"); if (parameter->isTemplateParameterPack()) { typeString += "..."; } return typeString; } std::string CxxDeclNameResolver::getTemplateParameterTypeString(const clang::TemplateTemplateParmDecl* parameter) { std::string templateParameterTypeString = "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) ? ", " : ""; } templateParameterTypeString += ">"; templateParameterTypeString += " typename"; // TODO: what if template template parameter is defined with class keyword? if (parameter->isTemplateParameterPack()) { templateParameterTypeString += "..."; } return templateParameterTypeString; } std::string CxxDeclNameResolver::getTemplateArgumentName(const clang::TemplateArgument& argument) { CxxTemplateArgumentNameResolver resolver(getCanonicalFilePathCache(), getIgnoredContextDecls()); return resolver.getTemplateArgumentName(argument); }