File indexing completed on 2024-04-28 09:33:46
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ContextUtils.h" 0008 #include "TypeUtils.h" 0009 #include "clazy_stl.h" 0010 0011 #include <clang/AST/Decl.h> 0012 #include <clang/AST/DeclBase.h> 0013 #include <clang/AST/DeclCXX.h> 0014 #include <clang/AST/DeclFriend.h> 0015 #include <clang/Basic/LLVM.h> 0016 #include <clang/Basic/SourceLocation.h> 0017 #include <clang/Basic/SourceManager.h> 0018 #include <clang/Basic/Specifiers.h> 0019 #include <llvm/Support/raw_ostream.h> 0020 0021 using namespace clang; 0022 0023 std::vector<DeclContext *> clazy::contextsForDecl(DeclContext *currentScope) 0024 { 0025 std::vector<DeclContext *> decls; 0026 decls.reserve(20); // jump-start 0027 while (currentScope) { 0028 decls.push_back(currentScope); 0029 currentScope = currentScope->getParent(); 0030 } 0031 0032 return decls; 0033 } 0034 0035 static std::string nameForContext(DeclContext *context) 0036 { 0037 if (auto *ns = dyn_cast<NamespaceDecl>(context)) { 0038 return ns->getNameAsString(); 0039 } 0040 if (auto *rec = dyn_cast<CXXRecordDecl>(context)) { 0041 return rec->getNameAsString(); 0042 } else if (auto *method = dyn_cast<CXXMethodDecl>(context)) { 0043 return method->getNameAsString(); 0044 } else if (isa<TranslationUnitDecl>(context)) { 0045 return {}; 0046 } else { 0047 llvm::errs() << "Unhandled kind: " << context->getDeclKindName() << "\n"; 0048 } 0049 0050 return {}; 0051 } 0052 0053 std::string clazy::getMostNeededQualifiedName(const SourceManager &sourceManager, 0054 CXXMethodDecl *method, 0055 DeclContext *currentScope, 0056 SourceLocation usageLoc, 0057 bool honourUsingDirectives) 0058 { 0059 if (!currentScope) { 0060 return method->getQualifiedNameAsString(); 0061 } 0062 0063 // All namespaces, classes, inner class qualifications 0064 auto methodContexts = clazy::contextsForDecl(method->getDeclContext()); 0065 0066 // Visible scopes in current scope 0067 auto visibleContexts = clazy::contextsForDecl(currentScope); 0068 0069 // Collect using directives 0070 std::vector<UsingDirectiveDecl *> usings; 0071 if (honourUsingDirectives) { 0072 for (DeclContext *context : visibleContexts) { 0073 clazy::append(context->using_directives(), usings); 0074 } 0075 } 0076 0077 for (UsingDirectiveDecl *u : usings) { 0078 NamespaceDecl *ns = u->getNominatedNamespace(); 0079 if (ns) { 0080 if (sourceManager.isBeforeInSLocAddrSpace(usageLoc, u->getBeginLoc())) { 0081 continue; 0082 } 0083 0084 visibleContexts.push_back(ns->getOriginalNamespace()); 0085 } 0086 } 0087 0088 for (DeclContext *context : visibleContexts) { 0089 if (context != method->getParent()) { // Don't remove the most immediate 0090 auto it = clazy::find_if(methodContexts, [context](DeclContext *c) { 0091 if (c == context) { 0092 return true; 0093 } 0094 auto *ns1 = dyn_cast<NamespaceDecl>(c); 0095 auto *ns2 = dyn_cast<NamespaceDecl>(context); 0096 return ns1 && ns2 && ns1->getQualifiedNameAsString() == ns2->getQualifiedNameAsString(); 0097 }); 0098 if (it != methodContexts.end()) { 0099 methodContexts.erase(it, it + 1); 0100 } 0101 } 0102 } 0103 0104 std::string neededContexts; 0105 for (DeclContext *context : methodContexts) { 0106 neededContexts = nameForContext(context) + "::" + neededContexts; 0107 } 0108 0109 const std::string result = neededContexts + method->getNameAsString(); 0110 return result; 0111 } 0112 0113 bool clazy::canTakeAddressOf(CXXMethodDecl *method, DeclContext *context, bool &isSpecialProtectedCase) 0114 { 0115 isSpecialProtectedCase = false; 0116 if (!method || !method->getParent()) { 0117 return false; 0118 } 0119 0120 if (method->getAccess() == clang::AccessSpecifier::AS_public) { 0121 return true; 0122 } 0123 0124 if (!context) { 0125 return false; 0126 } 0127 0128 CXXRecordDecl *contextRecord = nullptr; 0129 0130 do { 0131 contextRecord = dyn_cast<CXXRecordDecl>(context); 0132 context = context->getParent(); 0133 } while (contextRecord == nullptr && context); 0134 0135 if (!contextRecord) { // If we're not inside a class method we can't take the address of a private/protected method 0136 return false; 0137 } 0138 0139 CXXRecordDecl *record = method->getParent(); 0140 if (record == contextRecord) { 0141 return true; 0142 } 0143 0144 // We're inside a method belonging to a class (contextRecord). 0145 // Is contextRecord a friend of record ? Lets check: 0146 0147 for (auto *fr : record->friends()) { 0148 TypeSourceInfo *si = fr->getFriendType(); 0149 if (si) { 0150 const Type *t = si->getType().getTypePtrOrNull(); 0151 CXXRecordDecl *friendClass = t ? t->getAsCXXRecordDecl() : nullptr; 0152 if (friendClass == contextRecord) { 0153 return true; 0154 } 0155 } 0156 } 0157 0158 // There's still hope, lets see if the context is nested inside the class we're trying to access 0159 // Inner classes can access private members of outter classes. 0160 DeclContext *it = contextRecord; 0161 do { 0162 it = it->getParent(); 0163 if (it == record) { 0164 return true; 0165 } 0166 } while (it); 0167 0168 if (method->getAccess() == clang::AccessSpecifier::AS_private) { 0169 return false; 0170 } 0171 0172 if (method->getAccess() != clang::AccessSpecifier::AS_protected) { // shouldnt happen, must be protected at this point. 0173 return false; 0174 } 0175 0176 // For protected there's still hope, since record might be a derived or base class 0177 if (clazy::derivesFrom(record, contextRecord)) { 0178 return true; 0179 } 0180 0181 if (clazy::derivesFrom(contextRecord, record)) { 0182 isSpecialProtectedCase = true; 0183 return true; 0184 } 0185 0186 return false; 0187 }