bool clazy::canTakeAddressOf()

in src/ContextUtils.cpp [117:191]


bool clazy::canTakeAddressOf(CXXMethodDecl *method, DeclContext *context, bool &isSpecialProtectedCase)
{
    isSpecialProtectedCase = false;
    if (!method || !method->getParent()) {
        return false;
    }

    if (method->getAccess() == clang::AccessSpecifier::AS_public) {
        return true;
    }

    if (!context) {
        return false;
    }

    CXXRecordDecl *contextRecord = nullptr;

    do {
        contextRecord = dyn_cast<CXXRecordDecl>(context);
        context = context->getParent();
    } while (contextRecord == nullptr && context);

    if (!contextRecord) { // If we're not inside a class method we can't take the address of a private/protected method
        return false;
    }

    CXXRecordDecl *record = method->getParent();
    if (record == contextRecord) {
        return true;
    }

    // We're inside a method belonging to a class (contextRecord).
    // Is contextRecord a friend of record ? Lets check:

    for (auto *fr : record->friends()) {
        TypeSourceInfo *si = fr->getFriendType();
        if (si) {
            const Type *t = si->getType().getTypePtrOrNull();
            const CXXRecordDecl *friendClass = t ? t->getAsCXXRecordDecl() : nullptr;
            if (friendClass == contextRecord) {
                return true;
            }
        }
    }

    // There's still hope, lets see if the context is nested inside the class we're trying to access
    // Inner classes can access private members of outter classes.
    DeclContext *it = contextRecord;
    do {
        it = it->getParent();
        if (it == record) {
            return true;
        }
    } while (it);

    if (method->getAccess() == clang::AccessSpecifier::AS_private) {
        return false;
    }

    if (method->getAccess() != clang::AccessSpecifier::AS_protected) { // shouldnt happen, must be protected at this point.
        return false;
    }

    // For protected there's still hope, since record might be a derived or base class
    if (clazy::derivesFrom(record, contextRecord)) {
        return true;
    }

    if (clazy::derivesFrom(contextRecord, record)) {
        isSpecialProtectedCase = true;
        return true;
    }

    return false;
}