aboutsummaryrefslogtreecommitdiffstats
path: root/src/TypeUtils.h
blob: c2d9c5279a06bf26c549733621fe12f1f5b451c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
/*
   This file is part of the clazy static checker.

  Copyright (C) 2016-2017 Sergio Martins <smartins@kde.org>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#ifndef CLAZY_TYPE_UTILS_H
#define CLAZY_TYPE_UTILS_H

#include "clazy_export.h"

#include <clang/AST/Type.h>
#include <clang/AST/Expr.h>
#include <clang/AST/StmtCXX.h>
#include <clang/AST/DeclCXX.h>
#include <clang/AST/ASTContext.h>
#include <string>

namespace clang {
class CompilerInstance;
class Expr;
class LangOptions;
class QualType;
class Stmt;
class VarDecl;
class Type;
class CXXRecordDecl;
class CXXBaseSpecifier;
}

class ClazyContext;

namespace TypeUtils
{
    /**
     * Returns the sizeof(void*) for the platform we're compiling for, in bits.
     */
    inline int sizeOfPointer(const clang::ASTContext *context, clang::QualType qt)
    {
        if (!qt.getTypePtrOrNull())
            return -1;
        // HACK: What's a better way of getting the size of a pointer ?
        return context->getTypeSize(context->getPointerType(qt));
    }

    struct QualTypeClassification {
        bool isConst = false;
        bool isReference = false;
        bool isBig = false;
        bool isNonTriviallyCopyable = false;
        bool passBigTypeByConstRef = false;
        bool passNonTriviallyCopyableByConstRef = false;
        bool passSmallTrivialByValue = false;
        int size_of_T = 0;
    };

    /**
     * Classifies a QualType, for example:
     *
     * This function is useful to know if a type should be passed by value or const-ref.
     * The optional parameter body is in order to advise non-const-ref -> value, since the body
     * needs to be inspected to see if we that would compile.
     */
    CLAZYLIB_EXPORT bool classifyQualType(const ClazyContext *context, const clang::VarDecl *varDecl,
                                          QualTypeClassification &classification,
                                          clang::Stmt *body = nullptr);

    /**
     * If qt is a reference, return it without a reference.
     * If qt is not a reference, return qt.
     *
     * This is useful because sometimes you have an argument like "const QString &", but qualType.isConstQualified()
     * returns false. Must go through qualType->getPointeeType().isConstQualified().
     */
    inline clang::QualType unrefQualType(clang::QualType qualType)
    {
        const clang::Type *t = qualType.getTypePtrOrNull();
        return (t && t->isReferenceType()) ? t->getPointeeType() : qualType;
    }

    /**
     * If qt is a pointer or ref, return it without * or &.
     * Otherwise return qt unchanged.
     */
    inline clang::QualType pointeeQualType(clang::QualType qualType)
    {
        // TODO: Make this recursive when we need to remove more than one level of *
        const clang::Type *t = qualType.getTypePtrOrNull();
        return (t && (t->isReferenceType() || t->isPointerType())) ? t->getPointeeType() : qualType;
    }

    /**
     * Returns if @p arg is stack or heap allocated.
     * true means it is. false means it either isn't or the situation was too complex to judge.
     */
    CLAZYLIB_EXPORT void heapOrStackAllocated(clang::Expr *arg, const std::string &type,
                                              const clang::LangOptions &lo,
                                              bool &isStack, bool &isHeap);

    /**
     * Returns true if t is an AutoType that can't be deduced.
     */
    inline bool isUndeducibleAuto(const clang::Type *t)
    {
        if (!t)
            return false;

        auto at = llvm::dyn_cast<clang::AutoType>(t);
        return at && at->getDeducedType().isNull();
    }

    inline const clang::Type * unpealAuto(clang::QualType q)
    {
        if (q.isNull())
            return nullptr;

        if (auto t = llvm::dyn_cast<clang::AutoType>(q.getTypePtr()))
            return t->getDeducedType().getTypePtrOrNull();

        return q.getTypePtr();
    }

    /**
     * Returns true if childDecl is a descent from parentDecl
     **/
    CLAZYLIB_EXPORT bool derivesFrom(const clang::CXXRecordDecl *derived, const clang::CXXRecordDecl *possibleBase,
                                     std::vector<clang::CXXRecordDecl*> *baseClasses = nullptr);

    // Overload
    CLAZYLIB_EXPORT bool derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase);

    // Overload
    CLAZYLIB_EXPORT bool derivesFrom(clang::QualType derived, const std::string &possibleBase);

    /**
     * Returns the CXXRecordDecl represented by the CXXBaseSpecifier
     */
    inline clang::CXXRecordDecl * recordFromBaseSpecifier(const clang::CXXBaseSpecifier &base)
    {
        const clang::Type *t = base.getType().getTypePtrOrNull();
        return t ? t->getAsCXXRecordDecl() : nullptr;
    }
    /**
     * Returns true if the value is const. This is usually equivalent to qt.isConstQualified() but
     * takes care of the special case where qt represents a pointer. Many times you don't care if the
     * pointer is const or not and just care about the pointee.
     *
     * A a;        => false
     * const A a;  => true
     * A* a;       => false
     * const A* a; => true
     * A *const a; => false
     */
    inline bool valueIsConst(clang::QualType qt)
    {
        return pointeeQualType(qt).isConstQualified();
    }

    inline clang::CXXRecordDecl* typeAsRecord(clang::QualType qt)
    {
        if (qt.isNull())
            return nullptr;

        return qt->getAsCXXRecordDecl();
    }

    inline clang::CXXRecordDecl* typeAsRecord(clang::Expr *expr)
    {
        if (!expr)
            return nullptr;

        return typeAsRecord(pointeeQualType(expr->getType()));
    }

    inline clang::CXXRecordDecl* typeAsRecord(clang::ValueDecl *value)
    {
        if (!value)
            return nullptr;

        return typeAsRecord(pointeeQualType(value->getType()));
    }

    /**
     * Returns the class that the typedef refered by qt is in.
     *
     * class Foo {
     *     typedef A B;
     * };
     *
     * For the above example Foo would be returned.
     */
    inline clang::CXXRecordDecl* parentRecordForTypedef(clang::QualType qt)
    {
        auto t = qt.getTypePtrOrNull();
        if (!t)
            return nullptr;

        if (t->getTypeClass() == clang::Type::Typedef) {
            auto tdt = static_cast<const clang::TypedefType*>(t);
            clang::TypedefNameDecl *tdnd = tdt->getDecl();
            return llvm::dyn_cast_or_null<clang::CXXRecordDecl>(tdnd->getDeclContext());

        }

        return nullptr;
    }

    // Example: const QString &
    inline bool isConstRef(const clang::Type *t)
    {
        return t && t->isReferenceType() && t->getPointeeType().isConstQualified();
    }
}

#endif