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 }