File indexing completed on 2024-05-19 05:42:03

0001 // lvtclp_codebasedbvisitor.cpp                                       -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtclp_logicaldepvisitor.h>
0021 
0022 #include <ct_lvtmdb_componentobject.h>
0023 #include <ct_lvtmdb_errorobject.h>
0024 #include <ct_lvtmdb_fieldobject.h>
0025 #include <ct_lvtmdb_fileobject.h>
0026 #include <ct_lvtmdb_functionobject.h>
0027 #include <ct_lvtmdb_methodobject.h>
0028 #include <ct_lvtmdb_namespaceobject.h>
0029 #include <ct_lvtmdb_objectstore.h>
0030 #include <ct_lvtmdb_packageobject.h>
0031 #include <ct_lvtmdb_typeobject.h>
0032 #include <ct_lvtmdb_variableobject.h>
0033 
0034 #include <ct_lvtshr_graphenums.h>
0035 
0036 #include <ct_lvtshr_stringhelpers.h>
0037 
0038 #include <ct_lvtclp_clputil.h>
0039 
0040 #include <QDebug>
0041 
0042 #include <clang/AST/ASTContext.h>
0043 #include <clang/Basic/Diagnostic.h>
0044 #include <clang/Basic/Version.h>
0045 #include <clang/Frontend/CompilerInstance.h>
0046 #include <clang/Frontend/FrontendActions.h>
0047 #include <clang/Lex/PPCallbacks.h>
0048 #include <clang/Rewrite/Core/Rewriter.h>
0049 
0050 #include <filesystem>
0051 #include <llvm/Support/raw_ostream.h>
0052 
0053 #include <functional>
0054 #include <string>
0055 
0056 namespace {
0057 
0058 // clang::Type::isTypedefNameType (not in older clang)
0059 // Determines whether this type is written as a typedef-name.
0060 bool typeIsTypedefNameType(const clang::Type *type)
0061 {
0062     if (type->getAs<clang::TypedefType>()) {
0063         return true;
0064     }
0065     if (const auto *TST = type->getAs<clang::TemplateSpecializationType>()) {
0066         return TST->isTypeAlias();
0067     }
0068     return false;
0069 }
0070 
0071 // remove a prefix from the start of a string
0072 std::string removeStrPrefix(const std::string& str, const char *prefix)
0073 {
0074     const std::size_t loc = str.find(prefix);
0075     if (loc == 0) {
0076         return str.substr(strlen(prefix));
0077     }
0078     return str;
0079 }
0080 
0081 // Remove everyting in a string after a particular char
0082 std::string truncStrAfterChar(const std::string& str, const char c)
0083 {
0084     const std::size_t loc = str.find(c);
0085     if (loc != std::string::npos) {
0086         return str.substr(0, loc);
0087     }
0088     return str;
0089 }
0090 
0091 // Remove trailing spaces from string
0092 std::string trimTrailingSpaces(const std::string& str)
0093 {
0094     std::size_t loc = str.find_last_not_of(' ');
0095     return str.substr(0, loc + 1);
0096 }
0097 
0098 template<typename TARGET>
0099 void searchClangStmtAST(const clang::Stmt *top, std::function<void(const TARGET *)> visitor)
0100 // Recursively search AST starting at `top`, calling `visitor` on any
0101 // TARGETs we find
0102 {
0103     if (!top) {
0104         return;
0105     }
0106 
0107     auto *target = clang::dyn_cast<TARGET>(top);
0108     if (target) {
0109         visitor(target);
0110     }
0111 
0112     // recurse into statement children
0113     std::for_each(top->child_begin(), top->child_end(), [&visitor](const clang::Stmt *top) {
0114         searchClangStmtAST(top, visitor);
0115     });
0116 }
0117 
0118 } // unnamed namespace
0119 
0120 namespace Codethink::lvtclp {
0121 
0122 LogicalDepVisitor::LogicalDepVisitor(clang::ASTContext *Context,
0123                                      clang::StringRef file,
0124                                      lvtmdb::ObjectStore& memDb,
0125                                      std::filesystem::path prefix,
0126                                      std::vector<std::filesystem::path> nonLakosians,
0127                                      std::vector<std::pair<std::string, std::string>> thirdPartyDirs,
0128                                      std::shared_ptr<VisitLog> visitLog,
0129                                      std::shared_ptr<StaticFnHandler> staticFnHandler,
0130                                      std::optional<std::function<void(const std::string&, long)>> messageCallback,
0131                                      bool catchCodeAnalysisOutput):
0132     Context(Context),
0133     d_memDb(memDb),
0134     d_prefix(std::filesystem::weakly_canonical(prefix)),
0135     d_nonLakosianDirs(std::move(nonLakosians)),
0136     d_thirdPartyDirs(std::move(thirdPartyDirs)),
0137     d_visitLog_p(std::move(visitLog)),
0138     d_staticFnHandler_p(std::move(staticFnHandler)),
0139     d_messageCallback(std::move(messageCallback)),
0140     d_catchCodeAnalysisOutput(catchCodeAnalysisOutput)
0141 {
0142     sourceFilePtr = ClpUtil::writeSourceFile(file.str(), false, d_memDb, d_prefix, d_nonLakosianDirs, d_thirdPartyDirs);
0143 }
0144 
0145 bool LogicalDepVisitor::VisitNamespaceDecl(clang::NamespaceDecl *namespaceDecl)
0146 {
0147     if (d_visitLog_p->alreadyVisited(namespaceDecl, clang::Decl::Kind::Namespace)) {
0148         return true;
0149     }
0150 
0151     std::string name = namespaceDecl->getNameAsString();
0152 
0153     if (!namespaceDecl->hasExternalFormalLinkage() || name.empty()) {
0154         // anon namespace
0155         return true; // RETURN
0156     }
0157 
0158     std::string qualifiedName = namespaceDecl->getQualifiedNameAsString();
0159     std::string parentName = qualifiedName.substr(0, qualifiedName.length() - name.length() - 2);
0160 
0161     lvtmdb::NamespaceObject *namespacePtr = nullptr;
0162     d_memDb.withROLock([&] {
0163         namespacePtr = d_memDb.getNamespace(qualifiedName);
0164     });
0165     if (!namespacePtr) {
0166         lvtmdb::NamespaceObject *parentNamespace = nullptr;
0167         d_memDb.withRWLock([&] {
0168             parentNamespace = d_memDb.getNamespace(parentName);
0169             namespacePtr = d_memDb.getOrAddNamespace(qualifiedName, std::move(name), parentNamespace);
0170         });
0171         if (parentNamespace) {
0172             parentNamespace->withRWLock([&] {
0173                 parentNamespace->addChild(namespacePtr);
0174             });
0175         }
0176     }
0177 
0178     const clang::SourceManager& srcMgr = Context->getSourceManager();
0179     std::string sourceFile = ClpUtil::getRealPath(namespaceDecl->getLocation(), srcMgr);
0180     lvtmdb::FileObject *filePtr =
0181         ClpUtil::writeSourceFile(sourceFile, true, d_memDb, d_prefix, d_nonLakosianDirs, d_thirdPartyDirs);
0182     if (namespacePtr) {
0183         namespacePtr->withRWLock([&] {
0184             namespacePtr->addFile(filePtr);
0185         });
0186     }
0187     filePtr->withRWLock([&] {
0188         filePtr->addNamespace(namespacePtr);
0189     });
0190 
0191     return true;
0192 }
0193 
0194 bool LogicalDepVisitor::skipRecord(const clang::CXXRecordDecl *recordDecl)
0195 {
0196     // local class isn't architecturally significant
0197     // empty name => anon class
0198     // not external linkage => non-unique fully qualified name
0199     return recordDecl->isLocalClass() || recordDecl->getName().empty() || !recordDecl->hasExternalFormalLinkage();
0200 }
0201 
0202 lvtmdb::TypeObject *LogicalDepVisitor::lookupUDT(const clang::NamedDecl *decl, const char *warnMsg)
0203 {
0204     if (!decl) {
0205         return nullptr;
0206     }
0207     if (decl->getKind() == clang::Decl::Kind::CXXRecord) {
0208         const auto *recordDecl = clang::cast<clang::CXXRecordDecl>(decl);
0209         if (skipRecord(recordDecl)) {
0210             return nullptr;
0211         }
0212         // this is an internal clang type but clang doesn't recognise it as a
0213         // built-in type
0214         if (recordDecl->getQualifiedNameAsString() == "__va_list_tag") {
0215             return nullptr;
0216         }
0217     }
0218 
0219     const std::string qualifiedName = decl->getQualifiedNameAsString();
0220 
0221     lvtmdb::TypeObject *mdbUDT = nullptr;
0222     d_memDb.withROLock([&] {
0223         mdbUDT = d_memDb.getType(qualifiedName);
0224     });
0225 
0226     if (warnMsg && !mdbUDT) {
0227         std::string message = "WARN: lookupUDT for " + qualifiedName + " failed for " + warnMsg + "\n"
0228             + "      while processing decl at " + decl->getLocation().printToString(Context->getSourceManager()) + "\n";
0229 
0230         if (d_messageCallback) {
0231             auto& fnc = *d_messageCallback;
0232             fnc(message, ClpUtil::getThreadId());
0233         }
0234 
0235         d_memDb.withRWLock([&] {
0236             d_memDb.getOrAddError(lvtmdb::MdbUtil::ErrorKind::ParserError,
0237                                   qualifiedName,
0238                                   "lookupUDT failed",
0239                                   decl->getLocation().printToString(Context->getSourceManager()));
0240         });
0241     }
0242 
0243     return mdbUDT;
0244 }
0245 
0246 lvtmdb::TypeObject *LogicalDepVisitor::lookupParentRecord(const clang::CXXRecordDecl *record, const char *warnMsg)
0247 {
0248     if (!record) {
0249         return nullptr;
0250     }
0251 
0252     // base case
0253     if (!record->isLambda()) {
0254         return lookupUDT(record, warnMsg);
0255     }
0256 
0257     const clang::DeclContext *lambdaContext = record->getDeclContext();
0258     assert(lambdaContext);
0259 
0260     const clang::CXXRecordDecl *parent;
0261     const clang::CXXMethodDecl *method;
0262 
0263     switch (lambdaContext->getDeclKind()) {
0264     case clang::Decl::Kind::CXXMethod:
0265         method = clang::cast<clang::CXXMethodDecl>(lambdaContext);
0266         parent = method->getParent();
0267         break;
0268 
0269     case clang::Decl::Kind::CXXRecord:
0270         parent = clang::cast<clang::CXXRecordDecl>(lambdaContext);
0271         break;
0272 
0273     default:
0274         // I don't think there can be any other ways for a lambda to be inside
0275         // a class (lambdas as static fields don't seem to be allowed).
0276         parent = nullptr;
0277         break;
0278     }
0279 
0280     // recurse so we can handle lambdas-in-lambdas
0281     return lookupParentRecord(parent, warnMsg);
0282 }
0283 
0284 lvtmdb::TypeObject *LogicalDepVisitor::lookupType(const clang::Decl *decl,
0285                                                   clang::QualType qualType,
0286                                                   const char *warnMsg,
0287                                                   const bool resolveParent)
0288 {
0289     if (qualType.isNull()) {
0290         return nullptr;
0291     }
0292     // remove qualifiers
0293     qualType = qualType.getAtomicUnqualifiedType();
0294     qualType.removeLocalConst();
0295 
0296     if (qualType->isFunctionPointerType() || qualType->isFunctionReferenceType() || qualType->isMemberPointerType()
0297         || qualType->isMemberFunctionPointerType()) {
0298         // TODO
0299         return nullptr;
0300     }
0301 
0302     // skip some normalization of this is a type alias
0303     // (e.g. typedef void * Pointer) because if the
0304     // pointer-ness is removed from Pointer, it becomes a void. We want to look
0305     // up the UDT for Pointer instead. Likewise for similar transformations.
0306     if (!typeIsTypedefNameType(qualType.getTypePtr())) {
0307         // remove reference-ness
0308         if (qualType->isReferenceType()) {
0309             // recurse so that we go back to the start, removing qualifiers etc
0310             return lookupType(decl, qualType.getNonReferenceType(), warnMsg, resolveParent);
0311         }
0312         // skip built in types e.g. int
0313         if (qualType->isBuiltinType() && !typeIsTypedefNameType(qualType.getTypePtr())) {
0314             return nullptr;
0315         }
0316         // remove pointer-ness
0317         if (qualType->isPointerType() && !typeIsTypedefNameType(qualType.getTypePtr())) {
0318             // recurse because we can have pointers to arrays of pointers to pointers etc
0319             return lookupType(decl, qualType->getPointeeType(), warnMsg, resolveParent);
0320         }
0321     }
0322 
0323     if (qualType->isDependentType()) {
0324         const clang::TemplateSpecializationType *spec = qualType->getAs<clang::TemplateSpecializationType>();
0325         if (spec) {
0326             const clang::TemplateName tmpl = spec->getTemplateName();
0327 
0328             // Catch cases where qualType is a template parameter
0329             // e.g. B in:
0330             // template <template <class A> class B>
0331             // class C;
0332             //
0333             // We have to check that it is the template name that is dependent,
0334             // because we could have a real non-template param type which is
0335             // dependent because its template parameters are dependent.
0336             if (tmpl.isDependent()) {
0337                 return nullptr;
0338             }
0339 
0340             // Skip sugared types so that we don't resolve the typedef (or
0341             // whatever is going on here).
0342             // One example of this is re-exporting std::Type as bsl::Type.
0343             if (!qualType->getAs<clang::ElaboratedType>()) {
0344                 const auto *tmplDecl = tmpl.getAsTemplateDecl();
0345                 assert(tmplDecl);
0346 
0347                 if (const auto *classTempl = clang::dyn_cast<clang::ClassTemplateDecl>(tmplDecl)) {
0348                     clang::CXXRecordDecl *record = classTempl->getTemplatedDecl();
0349                     assert(record);
0350                     if (resolveParent) {
0351                         return lookupUDT(record, warnMsg);
0352                     }
0353                     return lookupParentRecord(record, warnMsg);
0354                 }
0355             }
0356         }
0357 
0358         // catch cases where clang cannot resolve the type (due to an expression
0359         // involving a template parameter, which could have a different type
0360         // in different template specializations).
0361         if (qualType.getAsString() == "<dependent type>") {
0362             return nullptr;
0363         }
0364 
0365         // catch raw usage of a template type param
0366         if (qualType->isTemplateTypeParmType()) {
0367             return nullptr;
0368         }
0369     }
0370 
0371     // catch arrays
0372     if (qualType->isArrayType()) {
0373         // Called "unsafe" because it discards qualifiers on the outer type.
0374         // We remove qualifiers anyway.
0375         const clang::ArrayType *array = qualType->getAsArrayTypeUnsafe();
0376         assert(array);
0377         return lookupType(decl, array->getElementType(), warnMsg, resolveParent);
0378     }
0379 
0380     // resolve decltype()
0381     if (qualType->isDecltypeType()) {
0382         const auto *declType = qualType->getAs<clang::DecltypeType>();
0383         assert(declType);
0384         clang::QualType resolvedType = declType->getUnderlyingType();
0385         return lookupType(decl, resolvedType, warnMsg, resolveParent);
0386     }
0387 
0388     // catch auto
0389     if (const auto *deducedType = qualType->getAs<clang::DeducedType>()) {
0390         clang::QualType target = deducedType->getDeducedType();
0391         if (!target.isNull()) {
0392             return lookupType(decl, target, warnMsg, resolveParent);
0393         }
0394         return nullptr;
0395     }
0396 
0397     // qualTypeToRecordDecl will resolve the typedef to the referenced type.
0398     // We don't want to do that so don't do this if we are looking at a typedef
0399     if (!typeIsTypedefNameType(qualType.getTypePtr())) {
0400         const clang::CXXRecordDecl *recordDecl = qualTypeToRecordDecl(qualType);
0401         if (recordDecl) {
0402             if (resolveParent) {
0403                 return lookupUDT(recordDecl, warnMsg);
0404             }
0405             return lookupParentRecord(recordDecl, warnMsg);
0406         }
0407     }
0408 
0409     // For typedefs we will have skipped the desugaring stages earlier. This can
0410     // leave types not fully qualified so we must resolve to a fully qualified
0411     // type here.
0412     if (const auto *elab = qualType->getAs<clang::ElaboratedType>()) {
0413         // weirdly, for dependent-typed things, clang will desugar to something
0414         // *less* qualified so skip those here. TODO!
0415         if (!qualType->isDependentType()) {
0416             clang::QualType namedType = elab->getNamedType();
0417             return lookupType(decl, namedType, warnMsg, resolveParent);
0418         }
0419     }
0420 
0421     if (qualType->getTypeClass() == clang::Type::DependentName) {
0422         // this name of a type cannot be resolved by clang within this context
0423         // e.g.
0424         // template <class Imp>
0425         // struct Foo;
0426         //
0427         // template <class IMP>
0428         // struct Bar {
0429         //     typedef Foo<IMP>::Int Int; // <---- Foo<IMP>::Int is a DependentNameType
0430         // };
0431         //
0432         // struct Marker1;
0433         // struct Marker2;
0434         //
0435         // template <>
0436         // struct Foo<Marker1> {
0437         //     typedef long Int; // <--- Foo<IMP> could be a long
0438         // };
0439         //
0440         // template <>
0441         // struct Foo<Marker2> {
0442         //     typedef int Int; // <--- Foo<IMP> could be an int
0443         // };
0444         //
0445         // Or Foo<IMP> could be something else in an as-yet unseen specialization
0446         //
0447         // TODO: one solution would be to synthesize a UDT on demand for
0448         //       Foo<class IMP>::Int because this is kind of like declaring that
0449         //       this will exist for all relevant specializations
0450         return nullptr;
0451     }
0452 
0453     // we couldn't do things the easy way. There are two cases here:
0454     //    1. template <typename T> C<T>: here we want to get C
0455     //    2. qualType is a a type alias, which we should look up by name
0456     //
0457     // We also have to remove "typename" from the type
0458 
0459     clang::PrintingPolicy policy(Context->getLangOpts());
0460     policy.adjustForCPlusPlus();
0461     policy.FullyQualifiedName = true;
0462     policy.PrintCanonicalTypes = false; // canonical-types resolves typedefs
0463     policy.IncludeTagDefinition = false;
0464     policy.PolishForDeclaration = true;
0465     policy.SuppressTagKeyword = true;
0466     policy.TerseOutput = true;
0467 
0468     std::string typeString = qualType.getAsString(policy);
0469 
0470     typeString = removeStrPrefix(typeString, "typename ");
0471 
0472     typeString = truncStrAfterChar(typeString, '<'); // case 1
0473 
0474     typeString = trimTrailingSpaces(typeString);
0475 
0476     // lookup for all cases
0477     lvtmdb::TypeObject *ret = nullptr;
0478     d_memDb.withROLock([&] {
0479         ret = d_memDb.getType(typeString);
0480     });
0481     if (!ret) {
0482         const std::string message = "WARN: lookupType for " + typeString + " failed for " + warnMsg + "\n"
0483             + "      while processing decl at " + decl->getLocation().printToString(Context->getSourceManager()) + "\n";
0484 
0485         if (d_messageCallback) {
0486             auto& fnc = *d_messageCallback;
0487             fnc(message, ClpUtil::getThreadId());
0488         }
0489 
0490         d_memDb.withRWLock([&] {
0491             d_memDb.getOrAddError(lvtmdb::MdbUtil::ErrorKind::ParserError,
0492                                   typeString,
0493                                   "lookupType failed",
0494                                   decl->getLocation().printToString(Context->getSourceManager()));
0495         });
0496     }
0497     return ret;
0498 }
0499 
0500 std::string LogicalDepVisitor::getReturnType(const clang::FunctionDecl *fnDecl)
0501 {
0502     // TODO this probably needs all of the normalization in lookupType
0503     //      and these should share code
0504 
0505     clang::QualType qualType = fnDecl->getReturnType();
0506     if (qualType.isNull()) {
0507         return "";
0508     }
0509     // remove qualifiers
0510     qualType = qualType.getAtomicUnqualifiedType();
0511     qualType.removeLocalConst();
0512 
0513     // remove typename keyword etc
0514     clang::PrintingPolicy policy(Context->getLangOpts());
0515     policy.adjustForCPlusPlus();
0516     policy.FullyQualifiedName = true;
0517     policy.PrintCanonicalTypes = true; // resolves typedefs
0518     policy.IncludeTagDefinition = false;
0519     policy.PolishForDeclaration = true;
0520     policy.SuppressTagKeyword = true;
0521     policy.TerseOutput = true;
0522 
0523     std::string typeString = qualType.getAsString(policy);
0524 
0525     typeString = removeStrPrefix(typeString, "typename ");
0526     typeString = truncStrAfterChar(typeString, '<');
0527     typeString = trimTrailingSpaces(typeString);
0528     return typeString;
0529 }
0530 
0531 void LogicalDepVisitor::processBaseClassTemplateArgs(const clang::Decl *decl,
0532                                                      lvtmdb::TypeObject *parent,
0533                                                      const clang::CXXRecordDecl::base_class_range& bases)
0534 {
0535     for (const clang::CXXBaseSpecifier& baseSpecifier : bases) {
0536         // if the base class is a template, we need to usesInTheInterface
0537         // the template arguments
0538         const auto *recordType = baseSpecifier.getType()->getAs<clang::RecordType>();
0539         if (!recordType) {
0540             continue;
0541         }
0542         const auto *recordDecl = recordType->getDecl()->getDefinition();
0543         const auto *const tmplate = clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(recordDecl);
0544         if (!tmplate) {
0545             // this base class isn't a template
0546             continue;
0547         }
0548 
0549         const clang::TemplateArgumentList& templateArgs = tmplate->getTemplateArgs();
0550         for (unsigned i = 0; i < templateArgs.size(); ++i) {
0551             const clang::TemplateArgument& arg = templateArgs[i];
0552             if (arg.getKind() != clang::TemplateArgument::ArgKind::Type) {
0553                 continue;
0554             }
0555 
0556             clang::QualType type = arg.getAsType();
0557             lvtmdb::TypeObject *mdbType = lookupType(decl, type, __func__, false);
0558             if (!mdbType) {
0559                 continue;
0560             }
0561 
0562             if (d_catchCodeAnalysisOutput) {
0563                 debugRelationship(parent, mdbType, decl, false, "templated base");
0564             }
0565             ClpUtil::addUsesInInter(parent, mdbType);
0566         }
0567     }
0568 }
0569 
0570 bool LogicalDepVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl *recordDecl)
0571 {
0572     if (d_visitLog_p->alreadyVisited(recordDecl,
0573                                      clang::Decl::Kind::CXXRecord,
0574                                      recordDecl->getTemplateSpecializationKind())) {
0575         return true;
0576     }
0577 
0578     if (skipRecord(recordDecl)) {
0579         return true;
0580     }
0581     if (classIsTemplateSpecialization(recordDecl)) {
0582         // still add source file so that we don't end up with cases where the
0583         // template is forward declared but only template specializations are
0584         // defined (never the original template): leading to there being no
0585         // source file for this class
0586         const auto *spec = clang::cast<clang::ClassTemplateSpecializationDecl>(recordDecl);
0587         assert(spec); // we just checked it is a template specialization
0588 
0589         const clang::ClassTemplateDecl *templateDecl = spec->getSpecializedTemplate();
0590         assert(templateDecl);
0591 
0592         lvtmdb::TypeObject *udt = lookupUDT(templateDecl, "expected to find template declaration for specialization");
0593         if (!udt) {
0594             return true;
0595         }
0596 
0597         addUDTSourceFile(udt, recordDecl);
0598         return true;
0599     }
0600 
0601     lvtshr::UDTKind recordKind;
0602     if (recordDecl->isClass()) {
0603         recordKind = lvtshr::UDTKind::Class;
0604     } else if (recordDecl->isStruct()) {
0605         recordKind = lvtshr::UDTKind::Struct;
0606     } else if (recordDecl->isUnion()) {
0607         recordKind = lvtshr::UDTKind::Union;
0608     } else {
0609         const std::string message = "WARN: clang::CXXRecordDecl which is not a class, struct or union on"
0610             + recordDecl->getQualifiedNameAsString() + "\n" + "at"
0611             + recordDecl->getLocation().printToString(Context->getSourceManager());
0612 
0613         if (d_messageCallback) {
0614             auto& fnc = *d_messageCallback;
0615             fnc(message, ClpUtil::getThreadId());
0616         }
0617 
0618         d_memDb.withRWLock([&] {
0619             d_memDb.getOrAddError(lvtmdb::MdbUtil::ErrorKind::ParserError,
0620                                   recordDecl->getQualifiedNameAsString(),
0621                                   "clang::CXXRecordDecl which is not a class, struct or union",
0622                                   recordDecl->getLocation().printToString(Context->getSourceManager()));
0623         });
0624 
0625         return true;
0626     }
0627 
0628     lvtmdb::TypeObject *userDefinedTypePtr = addUDT(recordDecl, recordKind);
0629     if (!userDefinedTypePtr) {
0630         return true;
0631     }
0632 
0633     if (recordDecl->hasDefinition()) {
0634         processBaseClassTemplateArgs(recordDecl, userDefinedTypePtr, recordDecl->bases());
0635 
0636         for (const auto& I : recordDecl->bases()) {
0637             const auto *Ty = I.getType()->getAs<clang::RecordType>();
0638             if (!Ty) {
0639                 continue;
0640             }
0641             const auto *Base = clang::cast<clang::CXXRecordDecl>(Ty->getDecl()->getDefinition());
0642 
0643             lvtmdb::TypeObject *parentClassPtr = lookupUDT(Base, "looking up record base class");
0644             if (parentClassPtr) {
0645                 lvtmdb::TypeObject::addIsARelationship(userDefinedTypePtr, parentClassPtr);
0646             }
0647         }
0648     }
0649 
0650     return true;
0651 }
0652 
0653 std::string LogicalDepVisitor::getTemplateParameters(const clang::FunctionDecl *functionDecl)
0654 {
0655     std::string templateParameters;
0656 
0657     if (functionDecl->getTemplatedKind() == clang::FunctionDecl::TK_FunctionTemplate) {
0658         clang::FunctionTemplateDecl *templateDecl = functionDecl->getDescribedFunctionTemplate();
0659 
0660         std::string arguments;
0661         clang::TemplateParameterList *parameterList = templateDecl->getTemplateParameters();
0662         for (auto *namedDecl : *parameterList) {
0663             const std::string argument = "typename " + namedDecl->getQualifiedNameAsString();
0664             if (arguments.empty()) {
0665                 arguments += argument;
0666             } else {
0667                 arguments += ", " + argument;
0668             }
0669         }
0670 
0671         templateParameters = "template <" + arguments + ">";
0672     }
0673 
0674     return templateParameters;
0675 }
0676 
0677 bool LogicalDepVisitor::VisitFunctionDecl(clang::FunctionDecl *functionDecl)
0678 {
0679     if (d_visitLog_p->alreadyVisited(functionDecl, clang::Decl::Kind::Function, functionDecl->getTemplatedKind())) {
0680         return true;
0681     }
0682 
0683     auto addFunctionToDb = [this](const clang::FunctionDecl *functionDecl) {
0684         std::string name = functionDecl->getNameAsString();
0685         std::string qualifiedName = functionDecl->getQualifiedNameAsString();
0686         std::string parentName = qualifiedName.substr(0, qualifiedName.length() - name.length() - 2);
0687         std::string signature = getSignature(functionDecl);
0688         std::string templateParameters = getTemplateParameters(functionDecl);
0689         std::string returnType = getReturnType(functionDecl);
0690 
0691         lvtmdb::FunctionObject *function = nullptr;
0692         lvtmdb::NamespaceObject *parentNamespace = nullptr;
0693         d_memDb.withRWLock([&] {
0694             parentNamespace = d_memDb.getNamespace(parentName);
0695             function = d_memDb.getOrAddFunction(qualifiedName,
0696                                                 std::move(name),
0697                                                 std::move(signature),
0698                                                 std::move(returnType),
0699                                                 std::move(templateParameters),
0700                                                 parentNamespace);
0701         });
0702         if (parentNamespace) {
0703             parentNamespace->withRWLock([&] {
0704                 parentNamespace->addFunction(function);
0705             });
0706         }
0707 
0708         // Heuristically assume that functions that have "_" suffix will have their definition in Fortran,
0709         // so accept the C counterpart.
0710         auto mayBeDefinedInFortran = qualifiedName.ends_with('_');
0711         if (functionDecl->isDefined() || mayBeDefinedInFortran) {
0712             // Only persist the associated file if it is where the function is _defined_
0713             // (not if it is merely _declared_).
0714             const clang::SourceManager& srcMgr = Context->getSourceManager();
0715             std::string sourceFile = ClpUtil::getRealPath(functionDecl->getLocation(), srcMgr);
0716             lvtmdb::FileObject *filePtr =
0717                 ClpUtil::writeSourceFile(sourceFile, false, d_memDb, d_prefix, d_nonLakosianDirs, d_thirdPartyDirs);
0718             filePtr->withRWLock([&] {
0719                 filePtr->addGlobalFunction(function);
0720             });
0721         }
0722         return function;
0723     };
0724 
0725     const auto *methodDecl = llvm::dyn_cast<clang::CXXMethodDecl>(functionDecl);
0726     if (methodDecl) {
0727         if (methodIsTemplateSpecialization(methodDecl)) {
0728             return true;
0729         }
0730         if (!methodDecl->hasExternalFormalLinkage()) {
0731             // internal linkage
0732             return true;
0733         }
0734 
0735         lvtmdb::TypeObject *parentClassPtr = lookupUDT(methodDecl->getParent(), "method parent class");
0736         if (!parentClassPtr) {
0737             return true;
0738         }
0739 
0740         std::string name = functionDecl->getNameAsString();
0741         std::string qualifiedName = functionDecl->getQualifiedNameAsString();
0742         std::string parentName = qualifiedName.substr(0, qualifiedName.length() - name.length() - 2);
0743         std::string signature = getSignature(functionDecl);
0744         std::string templateParameters = getTemplateParameters(functionDecl);
0745         std::string returnType = getReturnType(functionDecl);
0746 
0747         lvtmdb::MethodObject *methodPtr = nullptr;
0748         d_memDb.withROLock([&] {
0749             methodPtr = d_memDb.getMethod(qualifiedName, signature, templateParameters, returnType);
0750         });
0751         if (methodPtr) {
0752             return true; // RETURN
0753         }
0754 
0755         d_memDb.withRWLock([&] {
0756             methodPtr = d_memDb.getOrAddMethod(std::move(qualifiedName),
0757                                                std::move(name),
0758                                                std::move(signature),
0759                                                std::move(returnType),
0760                                                std::move(templateParameters),
0761                                                static_cast<lvtshr::AccessSpecifier>(methodDecl->getAccess()),
0762                                                methodDecl->isVirtual(),
0763                                                methodDecl->isPure(),
0764                                                methodDecl->isStatic(),
0765                                                methodDecl->isConst(),
0766                                                parentClassPtr);
0767         });
0768         parentClassPtr->withRWLock([&] {
0769             parentClassPtr->addMethod(methodPtr);
0770         });
0771     } else {
0772         if (funcIsTemplateSpecialization(functionDecl)) {
0773             return true;
0774         }
0775 
0776         if (!functionDecl->isDefined()) {
0777             return true;
0778         }
0779 
0780         auto *function = addFunctionToDb(functionDecl);
0781 
0782         // find out what classes this function uses in its implementation
0783         // and store them in the StaticFnHandler
0784         // (local var decls are handled separately)
0785         std::vector<lvtmdb::TypeObject *> usesInImpls =
0786             listChildStatementRelations(functionDecl, functionDecl->getBody(), nullptr);
0787         for (lvtmdb::TypeObject *dep : usesInImpls) {
0788             if (!dep) {
0789                 continue;
0790             }
0791             d_staticFnHandler_p->addFnUses(functionDecl, dep);
0792         }
0793 
0794         // find out if this function calls any functions
0795         auto visitCallExpr = [this, functionDecl, function, &addFunctionToDb](const clang::CallExpr *callExpr) {
0796             auto lock = function->readOnlyLock();
0797             (void) lock;
0798 
0799             const clang::FunctionDecl *callee = callExpr->getDirectCallee();
0800             if (!callee) {
0801                 return;
0802             }
0803             d_staticFnHandler_p->addFnUses(functionDecl, callee);
0804 
0805             if (callee->isCXXClassMember()) {
0806                 // We do not follow calls to methods. This may be reviewed.
0807                 return;
0808             }
0809 
0810             if (callee->isTemplateInstantiation()) {
0811                 auto *primaryTemplate = callee->getPrimaryTemplate();
0812                 if (primaryTemplate) {
0813                     callee = primaryTemplate->getTemplatedDecl();
0814                 }
0815             }
0816             auto *calledFunction = addFunctionToDb(callee);
0817             d_staticFnHandler_p->addCallgraphDep(function, calledFunction);
0818         };
0819         searchClangStmtAST<clang::CallExpr>(functionDecl->getBody(), visitCallExpr);
0820     }
0821 
0822     return true;
0823 }
0824 
0825 void LogicalDepVisitor::addField(lvtmdb::TypeObject *parent, const clang::ValueDecl *decl, const bool isStatic)
0826 {
0827     // anon member e.g.
0828     // struct Foo {
0829     //     unsigned one;
0830     //     int :32; // padding
0831     //     unsigned two;
0832     // };
0833     if (decl->getName().empty()) {
0834         return;
0835     }
0836     if (!parent) {
0837         return;
0838     }
0839 
0840     const std::string qualifiedName = decl->getQualifiedNameAsString();
0841 
0842     lvtmdb::FieldObject *fieldPtr = nullptr;
0843     d_memDb.withROLock([&] {
0844         fieldPtr = d_memDb.getField(qualifiedName);
0845     });
0846     if (fieldPtr) {
0847         // already added to the database
0848         return;
0849     }
0850 
0851     const clang::QualType qualType = decl->getType();
0852     const std::string typeString = qualType.getAsString();
0853 
0854     d_memDb.withRWLock([&] {
0855         fieldPtr = d_memDb.getOrAddField(qualifiedName,
0856                                          decl->getNameAsString(),
0857                                          typeString,
0858                                          static_cast<lvtshr::AccessSpecifier>(decl->getAccess()),
0859                                          isStatic,
0860                                          parent);
0861     });
0862     parent->withRWLock([&] {
0863         parent->addField(fieldPtr);
0864     });
0865 
0866     const clang::TemplateSpecializationType *templateTypePtr =
0867         qualType.getNonReferenceType()->getAs<clang::TemplateSpecializationType>();
0868     if (templateTypePtr) {
0869         parseFieldTemplate(decl, templateTypePtr, parent, fieldPtr);
0870     }
0871 
0872     const clang::CXXRecordDecl *fieldRecord = qualTypeToRecordDecl(qualType);
0873 
0874     if (fieldRecord && fieldRecord->isLambda()) {
0875         // this variable is a lambda function
0876         const clang::CXXMethodDecl *callOperator = fieldRecord->getLambdaCallOperator();
0877         assert(callOperator);
0878 
0879         for (const clang::ParmVarDecl *param : callOperator->parameters()) {
0880             // param is a parameter to a lambda function
0881             processMethodArg(param, parent, decl->getAccess());
0882         }
0883 
0884         addReturnRelation(callOperator, parent, decl->getAccess());
0885         return;
0886     }
0887     writeFieldRelations(decl, qualType, parent, fieldPtr);
0888 }
0889 
0890 bool LogicalDepVisitor::VisitFieldDecl(clang::FieldDecl *fieldDecl)
0891 {
0892     if (d_visitLog_p->alreadyVisited(fieldDecl, clang::Decl::Kind::Field)) {
0893         return true;
0894     }
0895 
0896     if (!fieldDecl->hasExternalFormalLinkage()) {
0897         // internal linkage
0898         return true;
0899     }
0900 
0901     clang::RecordDecl *record = fieldDecl->getParent();
0902     const auto *parentDecl = llvm::dyn_cast<clang::CXXRecordDecl>(record);
0903     if (!parentDecl) {
0904         return true;
0905     }
0906     if (classIsTemplateSpecialization(parentDecl)) {
0907         return true;
0908     }
0909 
0910     lvtmdb::TypeObject *parent = lookupParentRecord(parentDecl, "field parent");
0911     if (!parent) {
0912         return true;
0913     }
0914 
0915     // static fields are clang::VarDecls not clang::FieldDecls so this can never
0916     // be static
0917     addField(parent, fieldDecl, false);
0918     processChildStatements(fieldDecl, fieldDecl->getInClassInitializer(), parent);
0919 
0920     return true; // RETURN
0921 }
0922 
0923 bool LogicalDepVisitor::funcIsTemplateSpecialization(const clang::FunctionDecl *decl)
0924 // clang gives us a callback when a template function is defined and
0925 // again for each (explicit or implicit) specialization with concrete
0926 // types.
0927 //
0928 // We've already got all the information we need from parsing the template
0929 // definition. We don't want to parse with concrete types otherwise we
0930 // will create relationships for the types used in the specialization.
0931 {
0932     assert(decl);
0933 
0934     using TK = clang::FunctionDecl::TemplatedKind;
0935     const TK kind = decl->getTemplatedKind();
0936 
0937     return kind == TK::TK_MemberSpecialization || kind == TK::TK_FunctionTemplateSpecialization
0938         || kind == TK::TK_DependentFunctionTemplateSpecialization;
0939 }
0940 
0941 bool LogicalDepVisitor::classIsTemplateSpecialization(const clang::CXXRecordDecl *decl)
0942 {
0943     assert(decl);
0944     using TSK = clang::TemplateSpecializationKind;
0945     return decl->getTemplateSpecializationKind() != TSK::TSK_Undeclared;
0946 }
0947 
0948 bool LogicalDepVisitor::methodIsTemplateSpecialization(const clang::CXXMethodDecl *decl)
0949 {
0950     assert(decl);
0951     const clang::CXXRecordDecl *parent = decl->getParent();
0952 
0953     return funcIsTemplateSpecialization(decl) || classIsTemplateSpecialization(parent);
0954 }
0955 
0956 void LogicalDepVisitor::visitLocalVarDeclOrParam(clang::VarDecl *varDecl)
0957 {
0958     assert(varDecl);
0959 
0960     // what are we declared inside of?
0961     const clang::DeclContext *declContext = varDecl->getParentFunctionOrMethod();
0962     if (!declContext) {
0963         return;
0964     }
0965 
0966     const clang::CXXMethodDecl *methodDecl = nullptr;
0967     const clang::FunctionDecl *funcDecl = nullptr;
0968 
0969     if ((methodDecl = clang::dyn_cast<clang::CXXMethodDecl>(declContext))) {
0970         if (methodIsTemplateSpecialization(methodDecl)) {
0971             return;
0972         }
0973     } else {
0974         funcDecl = clang::dyn_cast<clang::FunctionDecl>(declContext);
0975     }
0976 
0977     // What matters is if the *method* is a template specialization, not the var.
0978     // Conceptually this should be seeded by methodDecl->getTemplatedKind()
0979     // but as we just return early after a trivial comparison, it is quicker
0980     // to do that first.
0981     if (d_visitLog_p->alreadyVisited(varDecl, clang::Decl::Kind::Var)) {
0982         return;
0983     }
0984 
0985     // what is our type?
0986     const clang::QualType qualType = varDecl->getType();
0987     lvtmdb::TypeObject *type = lookupType(varDecl, qualType, "varDecl type", false);
0988 
0989     if (methodDecl) {
0990         // look up parent class then add relation for that
0991         const clang::CXXRecordDecl *containingRecord = methodDecl->getParent();
0992         assert(containingRecord);
0993 
0994         lvtmdb::TypeObject *containerDecl = lookupParentRecord(containingRecord, "containing class for varDecl");
0995         if (!containerDecl) {
0996             return;
0997         }
0998 
0999         // special case: method parameters:
1000         if (!varDecl->isLocalVarDecl()) {
1001             // we only call this method if isLocalVarDeclOrParam therefore varDecl
1002             // must be a parameter
1003 
1004             processMethodArg(varDecl, containerDecl);
1005             return;
1006         }
1007 
1008         // Add "container usesInTheImplementation type" if it isn't already recorded
1009         if (type) {
1010             // AS_PRIVATE = always usesInImpl
1011             addRelation(containerDecl, qualType, varDecl, clang::AccessSpecifier::AS_private, "varDecl");
1012         }
1013     } else if (funcDecl) {
1014         // add pseudo function relationship
1015         if (type) {
1016             d_staticFnHandler_p->addFnUses(funcDecl, type);
1017         }
1018     }
1019 }
1020 
1021 bool LogicalDepVisitor::VisitVarDecl(clang::VarDecl *varDecl)
1022 {
1023     if (varDecl->isLocalVarDeclOrParm()) {
1024         visitLocalVarDeclOrParam(varDecl);
1025         return true; // RETURN
1026     }
1027 
1028     if (d_visitLog_p->alreadyVisited(varDecl, clang::Decl::Kind::Var, varDecl->getType()->isTemplateTypeParmType())) {
1029         return true;
1030     }
1031 
1032     if (varDecl->isStaticDataMember()) {
1033         const clang::DeclContext *context = varDecl->getDeclContext();
1034         // this cast can't fail because we already checked that we are a data
1035         // member
1036         const auto *record = clang::cast<clang::CXXRecordDecl>(context);
1037         if (classIsTemplateSpecialization(record)) {
1038             return true;
1039         }
1040 
1041         lvtmdb::TypeObject *parent = lookupParentRecord(record, "static data member parent");
1042         if (!parent) {
1043             return true;
1044         }
1045 
1046         addField(parent, varDecl, true);
1047         processChildStatements(varDecl, varDecl->getInit(), parent);
1048 
1049         return true;
1050     }
1051 
1052     std::string name = varDecl->getNameAsString();
1053     std::string qualifiedName = varDecl->getQualifiedNameAsString();
1054     std::string parentName = qualifiedName.substr(0, qualifiedName.length() - name.length() - 2);
1055 
1056     if (qualifiedName.empty()) {
1057         return true; // RETURN
1058     }
1059 
1060     lvtmdb::NamespaceObject *parentNamespace = nullptr;
1061 
1062     if (!parentName.empty() && parentName != name) {
1063         d_memDb.withROLock([&] {
1064             parentNamespace = d_memDb.getNamespace(parentName);
1065         });
1066         if (!parentNamespace) {
1067             return true; // RETURN
1068         }
1069     }
1070 
1071     lvtmdb::VariableObject *var = nullptr;
1072     d_memDb.withROLock([&] {
1073         var = d_memDb.getVariable(qualifiedName);
1074     });
1075     if (var) {
1076         return true; // RETURN
1077     }
1078 
1079     clang::QualType qualType = varDecl->getType();
1080 
1081     std::string typeString = qualType.getAsString();
1082 
1083     d_memDb.withRWLock([&] {
1084         var = d_memDb.getOrAddVariable(qualifiedName,
1085                                        std::move(name),
1086                                        std::move(typeString),
1087                                        !parentNamespace,
1088                                        parentNamespace);
1089     });
1090     if (parentNamespace) {
1091         parentNamespace->withRWLock([&] {
1092             parentNamespace->addVariable(var);
1093         });
1094     }
1095     return true;
1096 }
1097 
1098 const clang::CXXRecordDecl *LogicalDepVisitor::qualTypeToRecordDecl(const clang::QualType qualType)
1099 {
1100     const clang::CXXRecordDecl *declPtr = nullptr;
1101     const clang::Type *typePtr = qualType.getTypePtr();
1102 
1103     if (typePtr->isPointerType() || typePtr->isReferenceType()) {
1104         declPtr = typePtr->getPointeeCXXRecordDecl();
1105     } else {
1106         declPtr = typePtr->getAsCXXRecordDecl();
1107     }
1108 
1109     return declPtr;
1110 }
1111 
1112 void LogicalDepVisitor::processMethodArg(const clang::VarDecl *varDecl,
1113                                          lvtmdb::TypeObject *containerDecl,
1114                                          clang::AccessSpecifier access)
1115 {
1116     const clang::DeclContext *varContext = varDecl->getDeclContext();
1117     assert(varContext);
1118 
1119     if (varContext->getDeclKind() != clang::Decl::Kind::CXXMethod) {
1120         return;
1121     }
1122 
1123     const auto *methodDecl = clang::cast<clang::CXXMethodDecl>(varContext);
1124     assert(methodDecl);
1125 
1126     const clang::CXXRecordDecl *methodParent = methodDecl->getParent();
1127     assert(methodParent);
1128 
1129     // figure out what our access specifier should be (if one wasn't already
1130     // given to us)
1131     if (access == clang::AS_none) {
1132         if (methodParent->isLambda()) {
1133             if (methodParent->getDeclContext()->getDeclKind() != clang::Decl::Kind::CXXMethod) {
1134                 // This is a lambda as a class field so
1135                 // parentContextKind == clang::Decl::Kind::CXXRecord
1136                 // We get called for this once from addField (which sets the
1137                 // correct access specifier) and once by VisitVarDecl for the
1138                 // argument (from which we cannot figure out the access
1139                 // specifier). If we got here then this is the second call,
1140                 // which can be safely ignored because the argument is
1141                 // already added
1142                 return;
1143             }
1144 
1145             // This lambda is used inside a method so it should so it
1146             // should always be usesInTheImpl
1147             access = clang::AS_private;
1148         } else {
1149             // method parent is not a lambda so this is a normal method
1150             // argument and should use the access specifier of the method
1151             access = methodDecl->getAccess();
1152         }
1153     }
1154 
1155     if (access == clang::AS_none) {
1156         // we won't add anything later anyway so fail early without troubling
1157         // the database
1158         return;
1159     }
1160 
1161     // normal (non-lambda, not local class, etc) will have been already
1162     // added to the database, in which case we need to link the argument to
1163     // the method
1164     lvtmdb::MethodObject *methodDeclarationPtr = nullptr;
1165     if (!skipRecord(methodParent)) {
1166         d_memDb.withROLock([&] {
1167             methodDeclarationPtr = d_memDb.getMethod(methodDecl->getQualifiedNameAsString(),
1168                                                      getSignature(methodDecl),
1169                                                      getTemplateParameters(methodDecl),
1170                                                      getReturnType(methodDecl));
1171         });
1172         if (!methodDeclarationPtr) {
1173             if (d_messageCallback) {
1174                 const std::string message = "WARN: failed to look up method " + methodDecl->getQualifiedNameAsString()
1175                     + " to add method argument\n";
1176 
1177                 auto& fnc = *d_messageCallback;
1178                 fnc(message, ClpUtil::getThreadId());
1179             }
1180 
1181             d_memDb.withRWLock([&] {
1182                 d_memDb.getOrAddError(lvtmdb::MdbUtil::ErrorKind::ParserError,
1183                                       methodDecl->getQualifiedNameAsString(),
1184                                       "failed to look up method",
1185                                       methodDecl->getLocation().printToString(Context->getSourceManager()));
1186             });
1187         }
1188     }
1189 
1190     // if it is a template we also need to add all of the template parameters
1191     // to the database
1192     const clang::TemplateSpecializationType *templateTypePtr =
1193         varDecl->getType().getNonReferenceType()->getAs<clang::TemplateSpecializationType>();
1194     if (templateTypePtr) {
1195         parseArgumentTemplate(varDecl, templateTypePtr, access, containerDecl, methodDeclarationPtr);
1196     }
1197 
1198     // finally write the method-argument relation to the database
1199     const clang::QualType qualType = varDecl->getType();
1200     writeMethodArgRelation(varDecl, qualType, access, containerDecl, methodDeclarationPtr);
1201 
1202     // Process any expressions for initializing default arguments:
1203     if (varDecl->getKind() == clang::Decl::Kind::ParmVar) {
1204         const auto *paramVar = clang::cast<clang::ParmVarDecl>(varDecl);
1205         assert(paramVar);
1206 
1207         processChildStatements(varDecl, paramVar->getDefaultArg(), containerDecl);
1208     }
1209 }
1210 
1211 void LogicalDepVisitor::writeMethodArgRelation(const clang::Decl *decl,
1212                                                const clang::QualType type,
1213                                                const clang::AccessSpecifier access,
1214                                                lvtmdb::TypeObject *parentClassPtr,
1215                                                lvtmdb::MethodObject *methodDeclarationPtr)
1216 {
1217     lvtmdb::TypeObject *argClassPtr = lookupType(decl, type, __func__, false);
1218     if (argClassPtr && parentClassPtr && argClassPtr != parentClassPtr) {
1219         addRelation(parentClassPtr, type, decl, access, "method arg");
1220         if (methodDeclarationPtr) {
1221             methodDeclarationPtr->withRWLock([&] {
1222                 methodDeclarationPtr->addArgumentType(argClassPtr);
1223             });
1224         }
1225     }
1226 }
1227 
1228 void LogicalDepVisitor::parseArgumentTemplate(const clang::Decl *decl,
1229                                               const clang::TemplateSpecializationType *templateTypePtr,
1230                                               const clang::AccessSpecifier access,
1231                                               lvtmdb::TypeObject *parentClassPtr,
1232                                               lvtmdb::MethodObject *methodDeclarationPtr)
1233 {
1234     for (auto arg : templateTypePtr->template_arguments()) {
1235         if (arg.isDependent() || arg.getKind() == clang::TemplateArgument::Expression
1236             || arg.getKind() == clang::TemplateArgument::Template) {
1237             continue;
1238         }
1239 
1240         const clang::QualType qualType = arg.getAsType();
1241         writeMethodArgRelation(decl, qualType, access, parentClassPtr, methodDeclarationPtr);
1242 
1243         const clang::TemplateSpecializationType *ptr =
1244             qualType.getNonReferenceType()->getAs<clang::TemplateSpecializationType>();
1245 
1246         if (ptr != nullptr) {
1247             parseArgumentTemplate(decl, ptr, access, parentClassPtr, methodDeclarationPtr);
1248         }
1249     }
1250 }
1251 
1252 void LogicalDepVisitor::writeFieldRelations(const clang::Decl *decl,
1253                                             const clang::QualType fieldClass,
1254                                             lvtmdb::TypeObject *parentClassPtr,
1255                                             lvtmdb::FieldObject *fieldDeclarationPtr)
1256 {
1257     lvtmdb::TypeObject *fieldClassPtr = lookupType(decl, fieldClass, __func__, false);
1258     if (fieldClassPtr && parentClassPtr && fieldClassPtr != parentClassPtr) {
1259         clang::AccessSpecifier access = clang::AS_none;
1260         fieldDeclarationPtr->withROLock([&] {
1261             access = static_cast<clang::AccessSpecifier>(fieldDeclarationPtr->access());
1262         });
1263         addRelation(parentClassPtr, fieldClass, decl, access, "field");
1264 
1265         fieldDeclarationPtr->withRWLock([&] {
1266             fieldDeclarationPtr->addType(fieldClassPtr);
1267         });
1268     }
1269 }
1270 
1271 void LogicalDepVisitor::parseFieldTemplate(const clang::Decl *decl,
1272                                            const clang::TemplateSpecializationType *templateTypePtr,
1273                                            lvtmdb::TypeObject *parentClassPtr,
1274                                            lvtmdb::FieldObject *fieldDeclarationPtr)
1275 {
1276     for (auto arg : templateTypePtr->template_arguments()) {
1277         if (arg.isDependent() || arg.getKind() == clang::TemplateArgument::Expression
1278             || arg.getKind() == clang::TemplateArgument::Template) {
1279             continue;
1280         }
1281 
1282         clang::QualType qualType = arg.getAsType();
1283         writeFieldRelations(decl, qualType, parentClassPtr, fieldDeclarationPtr);
1284 
1285         const clang::TemplateSpecializationType *ptr =
1286             qualType.getNonReferenceType()->getAs<clang::TemplateSpecializationType>();
1287 
1288         if (ptr != nullptr) {
1289             parseFieldTemplate(decl, ptr, parentClassPtr, fieldDeclarationPtr);
1290         }
1291     }
1292 }
1293 
1294 std::string LogicalDepVisitor::getSignature(const clang::FunctionDecl *functionDecl)
1295 {
1296     std::string arguments{};
1297     for (auto *parameter : functionDecl->parameters()) {
1298         std::string argument = parameter->getType().getAsString() + " " + parameter->getNameAsString();
1299         if (parameter->hasDefaultArg()) {
1300             clang::SourceRange range = parameter->getDefaultArgRange();
1301             clang::SourceManager& srcMgr = Context->getSourceManager();
1302             llvm::StringRef s = clang::Lexer::getSourceText(clang::CharSourceRange::getTokenRange(range),
1303                                                             srcMgr,
1304                                                             Context->getLangOpts());
1305             argument += " = " + s.str();
1306         }
1307 
1308         if (arguments.empty()) {
1309             arguments += argument;
1310         } else {
1311             arguments += ", " + argument;
1312         }
1313     }
1314 
1315     return functionDecl->getNameAsString() + "(" + arguments + ")";
1316 }
1317 
1318 void LogicalDepVisitor::addReturnRelation(const clang::FunctionDecl *func,
1319                                           lvtmdb::TypeObject *parent,
1320                                           const clang::AccessSpecifier access)
1321 {
1322     assert(func);
1323     assert(parent);
1324 
1325     parseReturnTemplate(func, func->getReturnType(), parent, access);
1326 }
1327 
1328 void LogicalDepVisitor::parseReturnTemplate(const clang::Decl *decl,
1329                                             const clang::QualType returnQType,
1330                                             lvtmdb::TypeObject *parent,
1331                                             const clang::AccessSpecifier access)
1332 {
1333     if (returnQType->getTypeClass() == clang::Type::TypeClass::TemplateTypeParm) {
1334         return;
1335     }
1336 
1337     addRelation(parent, returnQType, decl, access, "method return");
1338 
1339     const clang::TemplateSpecializationType *ptr =
1340         returnQType.getNonReferenceType()->getAs<clang::TemplateSpecializationType>();
1341     if (ptr) {
1342         for (const auto arg : ptr->template_arguments()) {
1343             if (arg.getKind() == clang::TemplateArgument::ArgKind::Type) {
1344                 parseReturnTemplate(decl, arg.getAsType(), parent, access);
1345             }
1346         }
1347     }
1348 }
1349 
1350 bool LogicalDepVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl *methodDecl)
1351 {
1352     assert(methodDecl);
1353     if (d_visitLog_p->alreadyVisited(methodDecl, clang::Decl::Kind::CXXMethod, methodDecl->getTemplatedKind())) {
1354         return true;
1355     }
1356 
1357     if (!methodDecl->hasExternalFormalLinkage()) {
1358         // internal linkage
1359         return true;
1360     }
1361 
1362     if (methodIsTemplateSpecialization(methodDecl)) {
1363         return true;
1364     }
1365 
1366     lvtmdb::TypeObject *parent = lookupParentRecord(methodDecl->getParent(), "method parent");
1367     if (!parent) {
1368         return true;
1369     }
1370 
1371     processChildStatements(methodDecl, methodDecl->getBody(), parent);
1372     addReturnRelation(methodDecl, parent, methodDecl->getAccess());
1373 
1374     return true;
1375 }
1376 
1377 void LogicalDepVisitor::processChildStatements(const clang::Decl *decl,
1378                                                const clang::Stmt *top,
1379                                                lvtmdb::TypeObject *parent)
1380 // Wrap around listChildStatementRelations to add the relations
1381 {
1382     if (!parent) {
1383         return;
1384     }
1385 
1386     std::vector<lvtmdb::TypeObject *> usesInImpls = listChildStatementRelations(decl, top, parent);
1387     for (lvtmdb::TypeObject *dep : usesInImpls) {
1388         ClpUtil::addUsesInImpl(parent, dep);
1389     }
1390 }
1391 
1392 std::vector<lvtmdb::TypeObject *> LogicalDepVisitor::listChildStatementRelations(const clang::Decl *decl,
1393                                                                                  const clang::Stmt *top,
1394                                                                                  lvtmdb::TypeObject *parent)
1395 {
1396     std::vector<lvtmdb::TypeObject *> ret;
1397     if (!top) {
1398         return ret;
1399     }
1400 
1401     // what we actually want to do is VisitCXXTemporaryObjectExpr, but we need
1402     // to know which parentDecl the temporary object is inside, and I can't find
1403     // any neat way to do that. Instead, this method starts at top and
1404     // walks the AST looking for temporary objects.
1405     auto visitTemporary = [this, &parent, decl, &ret](const clang::Expr *temporary) {
1406         clang::QualType type = temporary->getType();
1407 
1408         lvtmdb::TypeObject *tempType = lookupType(decl, type, "temporary object type", false);
1409         if (!tempType) {
1410             return;
1411         }
1412 
1413         // Add "parent usesInTheImplementation tempType" if it isn't already recorded
1414         if (d_catchCodeAnalysisOutput) {
1415             debugRelationship(parent, tempType, decl, true, "temporary");
1416         }
1417         ret.push_back(tempType);
1418 
1419         // add any template arguments
1420         std::vector<lvtmdb::TypeObject *> args = getTemplateArguments(type, decl, "temporary template arg");
1421         for (lvtmdb::TypeObject *arg : args) {
1422             if (d_catchCodeAnalysisOutput) {
1423                 debugRelationship(parent, arg, decl, true, "temporary template arg");
1424             }
1425             ret.push_back(arg);
1426         }
1427     };
1428     searchClangStmtAST<clang::CXXTemporaryObjectExpr>(top, visitTemporary);
1429     // as far as we are concerned these are the same as a temporary variable
1430     // e.g.
1431     // template <typename T> void method() { return C<T>; }
1432     searchClangStmtAST<clang::CXXUnresolvedConstructExpr>(top, visitTemporary);
1433 
1434     // similarly to the above, we want to visit clang::DeclRefExpr to look for
1435     // references to static class members (we catch non-static things on variable
1436     // declaration)
1437     auto visitDeclRefExpr = [this, &parent, decl, &ret](const clang::DeclRefExpr *declRefExpr) {
1438         const clang::ValueDecl *valueDecl = declRefExpr->getDecl();
1439 
1440         // static method call
1441         const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(valueDecl);
1442         if (method) {
1443             const clang::CXXRecordDecl *targetDecl = method->getParent();
1444             assert(targetDecl);
1445             lvtmdb::TypeObject *target = lookupUDT(targetDecl, "static method call");
1446             if (!target) {
1447                 // sometimes we miss things
1448                 return;
1449             }
1450 
1451             if (d_catchCodeAnalysisOutput) {
1452                 debugRelationship(parent, target, decl, true, "static method call");
1453             }
1454             ret.push_back(target);
1455             return;
1456         }
1457 
1458         // static member variable reference
1459         const auto *varDecl = clang::dyn_cast<clang::VarDecl>(valueDecl);
1460         if (varDecl) {
1461             const clang::DeclContext *varContext = varDecl->getDeclContext();
1462             if (varContext->getDeclKind() != clang::Decl::Kind::CXXRecord) {
1463                 // we are referencing a variable which isn't a class member:
1464                 // ignore
1465                 return;
1466             }
1467 
1468             const auto *targetDecl = clang::cast<clang::CXXRecordDecl>(varContext);
1469             assert(targetDecl);
1470             lvtmdb::TypeObject *target = lookupUDT(targetDecl, "static field reference");
1471             if (!target) {
1472                 return;
1473             }
1474 
1475             if (d_catchCodeAnalysisOutput) {
1476                 debugRelationship(parent, target, decl, true, "static field reference");
1477             }
1478             ret.push_back(target);
1479             return;
1480         }
1481     };
1482     searchClangStmtAST<clang::DeclRefExpr>(top, visitDeclRefExpr);
1483 
1484     // similarly to the above, we want to visit clang::CallExpr to look for
1485     // templated function calls so we can record their template parameters
1486     // and to record calls to static functions
1487     auto visitCallExpr = [this, &parent, decl, &ret](const clang::CallExpr *declRefExpr) {
1488         const clang::FunctionDecl *func = declRefExpr->getDirectCallee();
1489         if (!func) {
1490             return;
1491         }
1492 
1493         // record call to the static function
1494         if (!func->isGlobal() && parent) {
1495             d_staticFnHandler_p->addUdtUses(parent, func);
1496         }
1497 
1498         // now handle template parameters
1499         if (!funcIsTemplateSpecialization(func)) {
1500             return;
1501         }
1502 
1503         // we need to record relations for the function template's parameters
1504         clang::FunctionTemplateSpecializationInfo *info = func->getTemplateSpecializationInfo();
1505         if (info) {
1506             const clang::TemplateArgumentList *args = info->TemplateArguments;
1507             assert(args);
1508             for (unsigned i = 0; i < args->size(); ++i) {
1509                 const clang::TemplateArgument& arg = args->get(i);
1510                 if (arg.getKind() != clang::TemplateArgument::Type) {
1511                     continue;
1512                 }
1513 
1514                 const clang::QualType qualType = arg.getAsType();
1515 
1516                 lvtmdb::TypeObject *target = lookupType(decl, qualType, "function call template parameter", false);
1517                 if (!target) {
1518                     continue;
1519                 }
1520 
1521                 if (d_catchCodeAnalysisOutput) {
1522                     debugRelationship(parent, target, decl, true, "template func parameter");
1523                 }
1524                 ret.push_back(target);
1525             }
1526         }
1527     };
1528     searchClangStmtAST<clang::CallExpr>(top, visitCallExpr);
1529 
1530     // Find calls to dependent scope member expr
1531     // e.g.
1532     // template <typename T>
1533     // ...
1534     // return D<T>::foo();
1535     // This is similar to clang::CXXUnresolvedConstructExpr but needs more wrapping
1536     auto visitDepScopeMember = [this, &parent, decl, &ret](const clang::CXXDependentScopeMemberExpr *expr) {
1537         std::string ss;
1538         llvm::raw_string_ostream s(ss);
1539         expr->printPretty(s, nullptr, clang::PrintingPolicy(Context->getLangOpts()));
1540         const std::string typeString = s.str();
1541 
1542         // we are expecting something looking like Type<TEMPLATE_PARAM>::method
1543         // clang can't extract Type from this (just saying <dependent type>)
1544         // but we can pull the string out and look for that in the database
1545         const std::size_t loc_template = typeString.find('<');
1546         if (loc_template == std::string::npos) {
1547             return;
1548         }
1549         const std::string className = typeString.substr(0, loc_template);
1550 
1551         lvtmdb::TypeObject *dest = nullptr;
1552         d_memDb.withROLock([&] {
1553             // we only have a string className, not any sort of clang::Decl
1554             // so we can't use lookup*
1555             dest = d_memDb.getType(className);
1556         });
1557         if (!dest) {
1558             // this is already an edge case of an edge case. Let's not stress
1559             // about a lookup failure here
1560             return;
1561         }
1562 
1563         if (d_catchCodeAnalysisOutput) {
1564             debugRelationship(parent, dest, decl, true, "CXXUnresolvedConstructExpr");
1565         }
1566         ret.push_back(dest);
1567     };
1568     searchClangStmtAST<clang::CXXDependentScopeMemberExpr>(top, visitDepScopeMember);
1569 
1570     return ret;
1571 }
1572 
1573 bool LogicalDepVisitor::VisitUsingDecl(clang::UsingDecl *usingDecl)
1574 {
1575     assert(usingDecl);
1576     // getKind() is okay here because we don't have callbacks at different levels
1577     // of the hierarchy
1578     if (d_visitLog_p->alreadyVisited(usingDecl, usingDecl->getKind())) {
1579         return true;
1580     }
1581 
1582     // We don't want to create a type alias for every name created by a using
1583     // declaration
1584     // Skip anything at file context (not in a namespace or a class)
1585     // TODO: maybe we should be more strict here?
1586     const clang::DeclContext *context = usingDecl->getDeclContext();
1587     assert(context);
1588     if (context->isTranslationUnit()) {
1589         return true;
1590     }
1591 
1592     for (const clang::UsingShadowDecl *shadowDecl : usingDecl->shadows()) {
1593         // get referenced type
1594         const clang::NamedDecl *referenced = shadowDecl->getTargetDecl();
1595         assert(referenced);
1596         const clang::Type *type = nullptr;
1597 
1598         const auto *typeDecl = clang::dyn_cast<clang::TypeDecl>(referenced);
1599         if (typeDecl) {
1600             type = typeDecl->getTypeForDecl();
1601         } else {
1602             // why isn't this a TypeDecl grr
1603             const auto *ctd = clang::dyn_cast<clang::ClassTemplateDecl>(referenced);
1604             if (ctd) {
1605                 const clang::CXXRecordDecl *record = ctd->getTemplatedDecl();
1606                 type = record->getTypeForDecl();
1607             }
1608         }
1609 
1610         if (type) {
1611             visitTypeAlias(usingDecl, type->getCanonicalTypeInternal());
1612         }
1613     }
1614 
1615     return true;
1616 }
1617 
1618 bool LogicalDepVisitor::VisitTypedefNameDecl(clang::TypedefNameDecl *nameDecl)
1619 {
1620     assert(nameDecl);
1621     // getKind() is okay here because we don't have callbacks at different levels of the hierarchy
1622     if (d_visitLog_p->alreadyVisited(nameDecl, nameDecl->getKind())) {
1623         return true;
1624     }
1625 
1626     // get referenced type
1627     const clang::QualType type = nameDecl->getUnderlyingType();
1628 
1629     visitTypeAlias(nameDecl, type);
1630 
1631     return true;
1632 }
1633 
1634 lvtmdb::TypeObject *LogicalDepVisitor::addUDT(const clang::NamedDecl *nameDecl, const lvtshr::UDTKind kind)
1635 {
1636     auto isInAnonNamespace = [](const clang::NamedDecl *nameDecl) -> bool {
1637         const clang::DeclContext *context = nameDecl->getDeclContext();
1638         while (context) {
1639             if (context->isNamespace()) {
1640                 const auto *nmspc = clang::cast<clang::NamespaceDecl>(context);
1641                 if (nmspc && nmspc->isAnonymousNamespace()) {
1642                     return true;
1643                 }
1644             }
1645             context = context->getParent();
1646         }
1647         return false;
1648     };
1649 
1650     // don't add UDTs in the anon namespace because they are not
1651     // significant and are not required to have a globally unique name
1652     if (isInAnonNamespace(nameDecl)) {
1653         return {};
1654     }
1655 
1656     lvtmdb::TypeObject *dbUDT = lookupUDT(nameDecl, nullptr);
1657     if (dbUDT) {
1658         addUDTSourceFile(dbUDT, nameDecl);
1659         return dbUDT;
1660     }
1661 
1662     // check if any parent contexts are template specializations, if so skip
1663     const clang::DeclContext *contextIter = nameDecl->getDeclContext();
1664     while (contextIter) {
1665         if (contextIter->isNamespace()) {
1666             // we have run out of parent classes
1667             break;
1668         }
1669         if (contextIter->getDeclKind() == clang::Decl::Kind::CXXRecord) {
1670             const auto *parentRecord = clang::cast<clang::CXXRecordDecl>(contextIter);
1671             assert(parentRecord);
1672             if (classIsTemplateSpecialization(parentRecord)) {
1673                 return nullptr;
1674             }
1675         }
1676         if (contextIter->getDeclKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
1677             return nullptr;
1678         }
1679         contextIter = contextIter->getParent();
1680     }
1681 
1682     // get parent context
1683     lvtmdb::TypeObject *parentClass = nullptr;
1684     lvtmdb::NamespaceObject *parentNamespace = nullptr;
1685     const clang::DeclContext *declContext = nameDecl->getDeclContext();
1686     const auto contextKind = declContext->getDeclKind();
1687     if (contextKind == clang::Decl::Kind::CXXRecord) {
1688         // lexically scoped within a class
1689         const auto *parentRecord = clang::cast<clang::CXXRecordDecl>(declContext);
1690 
1691         parentClass = lookupParentRecord(parentRecord, "TypeDefDecl parent");
1692         if (parentClass) {
1693             parentClass->withROLock([&] {
1694                 parentNamespace = parentClass->parentNamespace();
1695             });
1696         }
1697     } else if (contextKind == clang::Decl::Kind::Namespace) {
1698         // lexically scoped within a namespace
1699         const auto *nmspc = clang::cast<clang::NamespaceDecl>(declContext);
1700         assert(nmspc);
1701         d_memDb.withROLock([&] {
1702             parentNamespace = d_memDb.getNamespace(nmspc->getQualifiedNameAsString());
1703         });
1704     }
1705 
1706     d_memDb.withRWLock([&] {
1707         dbUDT = d_memDb.getOrAddType(nameDecl->getQualifiedNameAsString(),
1708                                      nameDecl->getNameAsString(),
1709                                      kind,
1710                                      static_cast<lvtshr::AccessSpecifier>(nameDecl->getAccess()),
1711                                      parentNamespace,
1712                                      nullptr, // parent package, see addUDTSourceFile
1713                                      parentClass);
1714     });
1715 
1716     if (parentNamespace) {
1717         parentNamespace->withRWLock([&] {
1718             parentNamespace->addType(dbUDT);
1719         });
1720     }
1721     if (parentClass) {
1722         parentClass->withRWLock([&] {
1723             parentClass->addChild(dbUDT);
1724         });
1725     }
1726 
1727     addUDTSourceFile(dbUDT, nameDecl);
1728 
1729     return dbUDT;
1730 }
1731 
1732 void LogicalDepVisitor::addUDTSourceFile(lvtmdb::TypeObject *udt, const clang::Decl *decl)
1733 {
1734     assert(udt);
1735     const std::string sourceFile = ClpUtil::getRealPath(decl->getLocation(), Context->getSourceManager());
1736 
1737     lvtmdb::FileObject *filePtr =
1738         ClpUtil::writeSourceFile(sourceFile, true, d_memDb, d_prefix, d_nonLakosianDirs, d_thirdPartyDirs);
1739     if (!filePtr) {
1740         return;
1741     }
1742     lvtmdb::ComponentObject *component = nullptr;
1743     lvtmdb::PackageObject *package = nullptr;
1744     filePtr->withROLock([&] {
1745         component = filePtr->component();
1746         package = filePtr->package();
1747     });
1748 
1749     udt->withRWLock([&] {
1750         udt->addFile(filePtr);
1751         if (component) {
1752             udt->addComponent(component);
1753         }
1754         if (package) {
1755             udt->setPackage(package);
1756         }
1757     });
1758 
1759     filePtr->withRWLock([&] {
1760         filePtr->addType(udt);
1761     });
1762 
1763     if (component) {
1764         component->withRWLock([&] {
1765             component->addType(udt);
1766         });
1767     }
1768 
1769     if (package) {
1770         package->withRWLock([&] {
1771             package->addType(udt);
1772         });
1773     }
1774 }
1775 
1776 void LogicalDepVisitor::visitTypeAlias(const clang::NamedDecl *nameDecl, const clang::QualType referencedType)
1777 {
1778     // check if the type alias is publically accessible so that we don't
1779     // end up adding a local type alias to the global namespace
1780     const clang::DeclContext *context = nameDecl->getDeclContext();
1781     assert(context);
1782     if (context->isFunctionOrMethod()) {
1783         // we still need a uses-in-impl if we are inside a method
1784         if (const auto *const method = clang::dyn_cast<clang::CXXMethodDecl>(context)) {
1785             const clang::CXXRecordDecl *parentClass = method->getParent();
1786             assert(parentClass);
1787 
1788             lvtmdb::TypeObject *parent = lookupParentRecord(parentClass, "type alias in method parent class");
1789             if (parent) {
1790                 // AS_PRIVATE = always usesInImpl
1791                 addRelation(parent, referencedType, nameDecl, clang::AccessSpecifier::AS_private, "local type alias");
1792             }
1793         }
1794         return;
1795     }
1796 
1797     auto *dbUDT = addUDT(nameDecl, lvtshr::UDTKind::TypeAlias);
1798     if (!dbUDT) {
1799         return;
1800     }
1801     // TODO: the aliased type will never have methods, fields etc
1802     //       this is technically correct but could be surprising when the fact
1803     //       that this is a typedef is an implementation detail
1804 
1805     // AS_private = always usesInImpl
1806     addRelation(dbUDT, referencedType, nameDecl, clang::AccessSpecifier::AS_private, "type alias 1");
1807 
1808     lvtmdb::TypeObject *parentClass = nullptr;
1809     dbUDT->withROLock([&] {
1810         parentClass = dbUDT->parent();
1811     });
1812     if (parentClass) {
1813         addRelation(parentClass, dbUDT, nameDecl, nameDecl->getAccess(), "type alias 2");
1814     }
1815 }
1816 
1817 bool LogicalDepVisitor::VisitEnumDecl(clang::EnumDecl *enumDecl)
1818 {
1819     assert(enumDecl);
1820     if (d_visitLog_p->alreadyVisited(enumDecl, enumDecl->getKind())) {
1821         return true;
1822     }
1823 
1824     // skip anon enum
1825     if (enumDecl->getNameAsString().empty()) {
1826         return true;
1827     }
1828 
1829     // skip internal linkage
1830     if (!enumDecl->hasExternalFormalLinkage()) {
1831         return true;
1832     }
1833 
1834     addUDT(enumDecl, lvtshr::UDTKind::Enum);
1835 
1836     return true;
1837 }
1838 
1839 std::vector<lvtmdb::TypeObject *>
1840 LogicalDepVisitor::getTemplateArguments(const clang::QualType type, const clang::Decl *decl, const char *desc)
1841 {
1842     // find any template arguments of the type
1843     auto *tmplSpec = clang::dyn_cast<clang::TemplateSpecializationType>(type);
1844 
1845     // llvm15< works with the clang::dyn_cast, and llvm16 works with getAs.
1846     if (!tmplSpec) {
1847         tmplSpec = type->getAs<clang::TemplateSpecializationType>();
1848     }
1849 
1850     if (!tmplSpec) {
1851         return {};
1852     }
1853 
1854     std::vector<lvtmdb::TypeObject *> templateArgs;
1855     const clang::ArrayRef<clang::TemplateArgument> args = tmplSpec->template_arguments();
1856 
1857     for (unsigned i = 0; i < args.size(); ++i) {
1858         if (args[i].getKind() != clang::TemplateArgument::ArgKind::Type) {
1859             continue;
1860         }
1861         clang::QualType argQType = args[i].getAsType();
1862 
1863         lvtmdb::TypeObject *argType = lookupType(decl, argQType, desc, false);
1864         if (argType) {
1865             templateArgs.push_back(argType);
1866         }
1867     }
1868 
1869     return templateArgs;
1870 }
1871 
1872 void LogicalDepVisitor::addRelation(lvtmdb::TypeObject *source,
1873                                     lvtmdb::TypeObject *target,
1874                                     const clang::Decl *decl,
1875                                     const clang::AccessSpecifier access,
1876                                     const char *desc)
1877 {
1878     if (access == clang::AS_public || access == clang::AS_protected) {
1879         if (d_catchCodeAnalysisOutput) {
1880             debugRelationship(source, target, decl, false, desc);
1881         }
1882         ClpUtil::addUsesInInter(source, target);
1883     } else if (access == clang::AS_private) {
1884         if (d_catchCodeAnalysisOutput) {
1885             debugRelationship(source, target, decl, true, desc);
1886         }
1887         ClpUtil::addUsesInImpl(source, target);
1888     } else {
1889         std::string message = "WARN: unknown access specifier (clang::AccessSpecifier)" + std::to_string(access);
1890         if (desc) {
1891             message += " for " + std::string(desc);
1892         }
1893         if (decl) {
1894             message += " at " + decl->getLocation().printToString(Context->getSourceManager());
1895         }
1896         message += "\n";
1897 
1898         if (d_messageCallback) {
1899             auto& fnc = *d_messageCallback;
1900             fnc(message, ClpUtil::getThreadId());
1901         }
1902     }
1903 }
1904 
1905 void LogicalDepVisitor::addRelation(lvtmdb::TypeObject *source,
1906                                     const clang::QualType targetType,
1907                                     const clang::Decl *decl,
1908                                     const clang::AccessSpecifier access,
1909                                     const char *desc)
1910 {
1911     bool usesInImpl;
1912     if (access == clang::AS_public || access == clang::AS_protected) {
1913         usesInImpl = false;
1914     } else if (access == clang::AS_private) {
1915         usesInImpl = true;
1916     } else {
1917         return;
1918     }
1919 
1920     lvtmdb::TypeObject *target = lookupType(decl, targetType, desc, false);
1921     if (target) {
1922         addRelation(source, target, decl, access, desc);
1923     }
1924 
1925     // handle if target has template arguments
1926     std::vector<lvtmdb::TypeObject *> args = getTemplateArguments(targetType, decl, desc);
1927     for (lvtmdb::TypeObject *arg : args) {
1928         if (d_catchCodeAnalysisOutput) {
1929             debugRelationship(source, arg, decl, usesInImpl, desc);
1930         }
1931         if (usesInImpl) {
1932             ClpUtil::addUsesInImpl(source, arg);
1933         } else {
1934             ClpUtil::addUsesInInter(source, arg);
1935         }
1936     }
1937 }
1938 
1939 void LogicalDepVisitor::debugRelationship(
1940     lvtmdb::TypeObject *source, lvtmdb::TypeObject *target, const clang::Decl *decl, bool impl, const char *desc)
1941 {
1942     // TODO: Add QLoggingCategory enabled test for early return
1943     if (!source || !target) {
1944         return;
1945     }
1946 
1947     source->withROLock([&] {
1948         qDebug() << QString::fromStdString(source->qualifiedName());
1949     });
1950     if (impl) {
1951         qDebug() << " (uses in the implementation) ";
1952     } else {
1953         qDebug() << " (uses in the interface) ";
1954     }
1955     target->withROLock([&] {
1956         qDebug() << QString::fromStdString(target->qualifiedName());
1957     });
1958 
1959     qDebug() << " (" << desc << ") ";
1960     if (decl) {
1961         auto contextStr =
1962             QString::fromStdString(decl->getLocation().printToString(decl->getASTContext().getSourceManager()));
1963 
1964         qDebug() << contextStr;
1965     }
1966     qDebug() << "\n";
1967 }
1968 
1969 } // end namespace Codethink::lvtclp