File indexing completed on 2024-04-21 05:38:47

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 }