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