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