Update git submodules
[LibreOffice.git] / compilerplugins / clang / check.cxx
blob60e476cc37b7a215e5d8a2fee2c41cb14e082d9d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <cassert>
12 #include <clang/AST/DeclCXX.h>
13 #include <clang/AST/DeclTemplate.h>
15 #include "check.hxx"
17 namespace loplugin {
19 TypeCheck TypeCheck::NonConst() const {
20 return !type_.isNull() && !type_.isConstQualified()
21 ? *this : TypeCheck();
22 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
23 // may look tempting, but could remove sugar we might be interested in
24 // checking for
27 TypeCheck TypeCheck::NonConstVolatile() const {
28 return
29 (!type_.isNull() && !type_.isConstQualified()
30 && !type_.isVolatileQualified())
31 ? *this : TypeCheck();
32 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
33 // may look tempting, but could remove sugar we might be interested in
34 // checking for
37 TypeCheck TypeCheck::Const() const {
38 return
39 (!type_.isNull() && type_.isConstQualified()
40 && !type_.isVolatileQualified())
41 ? *this : TypeCheck();
42 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
43 // may look tempting, but could remove sugar we might be interested in
44 // checking for
47 TypeCheck TypeCheck::Volatile() const {
48 return
49 (!type_.isNull() && !type_.isConstQualified()
50 && type_.isVolatileQualified())
51 ? *this : TypeCheck();
52 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
53 // may look tempting, but could remove sugar we might be interested in
54 // checking for
57 TypeCheck TypeCheck::ConstVolatile() const {
58 return
59 (!type_.isNull() && type_.isConstQualified()
60 && type_.isVolatileQualified())
61 ? *this : TypeCheck();
62 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
63 // may look tempting, but could remove sugar we might be interested in
64 // checking for
67 TypeCheck TypeCheck::ConstNonVolatile() const {
68 return
69 (!type_.isNull() && type_.isConstQualified()
70 && !type_.isVolatileQualified())
71 ? *this : TypeCheck();
72 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
73 // may look tempting, but could remove sugar we might be interested in
74 // checking for
77 TerminalCheck TypeCheck::Void() const {
78 return TerminalCheck(
79 !type_.isNull()
80 && type_->isSpecificBuiltinType(clang::BuiltinType::Void));
83 TerminalCheck TypeCheck::Char() const {
84 return TerminalCheck(
85 !type_.isNull()
86 && (type_->isSpecificBuiltinType(clang::BuiltinType::Char_S)
87 || type_->isSpecificBuiltinType(clang::BuiltinType::Char_U)));
90 TerminalCheck TypeCheck::AnyBoolean() const {
91 if (type_->isBooleanType()) {
92 return TerminalCheck(true);
94 auto t = type_->getAs<clang::TypedefType>();
95 if (t == nullptr) {
96 return TerminalCheck(false);
98 auto n =t->getDecl()->getName();
99 return TerminalCheck(
100 n == "sal_Bool" || n == "BOOL" || n == "Boolean" || n == "FT_Bool"
101 || n == "FcBool" || n == "GLboolean" || n == "NPBool" || n == "TW_BOOL"
102 || n == "UBool" || n == "boolean" || n == "dbus_bool_t"
103 || n == "gboolean" || n == "hb_bool_t" || n == "jboolean" || n == "my_bool");
106 TypeCheck TypeCheck::LvalueReference() const {
107 if (!type_.isNull()) {
108 auto const t = type_->getAs<clang::LValueReferenceType>();
109 if (t != nullptr) {
110 return TypeCheck(t->getPointeeType());
113 return TypeCheck();
116 TypeCheck TypeCheck::RvalueReference() const {
117 if (!type_.isNull()) {
118 auto const t = type_->getAs<clang::RValueReferenceType>();
119 if (t != nullptr) {
120 return TypeCheck(t->getPointeeType());
123 return TypeCheck();
126 TypeCheck TypeCheck::Pointer() const {
127 if (!type_.isNull()) {
128 auto const t = type_->getAs<clang::PointerType>();
129 if (t != nullptr) {
130 return TypeCheck(t->getPointeeType());
133 return TypeCheck();
136 TypeCheck TypeCheck::MemberPointerOf() const {
137 if (!type_.isNull()) {
138 auto const t = type_->getAs<clang::MemberPointerType>();
139 if (t != nullptr) {
140 return TypeCheck(t->getClass());
143 return TypeCheck();
146 TerminalCheck TypeCheck::Enum() const {
147 if (!type_.isNull()) {
148 auto const t = type_->getAs<clang::EnumType>();
149 if (t != nullptr) {
150 return TerminalCheck(true);
153 return TerminalCheck(false);
156 TypeCheck TypeCheck::Typedef() const {
157 if (!type_.isNull()) {
158 if (auto const t = type_->getAs<clang::TypedefType>()) {
159 return TypeCheck(t->desugar());
162 return TypeCheck();
165 DeclCheck TypeCheck::TemplateSpecializationClass() const {
166 if (!type_.isNull()) {
167 if (auto const t = type_->getAs<clang::TemplateSpecializationType>()) {
168 if (!t->isTypeAlias()) {
169 if (auto const d = llvm::dyn_cast_or_null<clang::ClassTemplateDecl>(
170 t->getTemplateName().getAsTemplateDecl()))
172 return DeclCheck(d->getTemplatedDecl());
177 return DeclCheck();
180 TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const {
181 return
182 (!type_.isNull()
183 && type_->getAs<clang::SubstTemplateTypeParmType>() == nullptr)
184 ? *this : TypeCheck();
187 ContextCheck DeclCheck::Operator(clang::OverloadedOperatorKind op) const {
188 assert(op != clang::OO_None);
189 auto f = llvm::dyn_cast_or_null<clang::FunctionDecl>(decl_);
190 return ContextCheck(
191 f != nullptr && f->getOverloadedOperator() == op
192 ? f->getDeclContext() : nullptr);
195 ContextCheck DeclCheck::MemberFunction() const {
196 auto m = llvm::dyn_cast_or_null<clang::CXXMethodDecl>(decl_);
197 return ContextCheck(m == nullptr ? nullptr : m->getParent());
200 namespace {
202 bool isGlobalNamespace(clang::DeclContext const * context) {
203 assert(context != nullptr);
204 return context->getEnclosingNamespaceContext()->isTranslationUnit();
209 TerminalCheck ContextCheck::GlobalNamespace() const {
210 return TerminalCheck(context_ != nullptr && isGlobalNamespace(context_));
213 TerminalCheck ContextCheck::StdNamespace() const {
214 return TerminalCheck(
215 context_ != nullptr && lookThroughLinkageSpec()->isStdNamespace());
218 namespace {
220 bool isStdOrNestedNamespace(clang::DeclContext const * context) {
221 assert(context != nullptr);
222 if (!context->isNamespace()) {
223 return false;
225 if (isGlobalNamespace(context)) {
226 return false;
228 if (context->isStdNamespace()) {
229 return true;
231 return isStdOrNestedNamespace(context->getParent());
236 TerminalCheck ContextCheck::StdOrNestedNamespace() const {
237 return TerminalCheck(context_ != nullptr && isStdOrNestedNamespace(lookThroughLinkageSpec()));
240 ContextCheck ContextCheck::AnonymousNamespace() const {
241 auto n = llvm::dyn_cast_or_null<clang::NamespaceDecl>(lookThroughLinkageSpec());
242 return ContextCheck(
243 n != nullptr && n->isAnonymousNamespace() ? n->getParent() : nullptr);
246 clang::DeclContext const * ContextCheck::lookThroughLinkageSpec() const {
247 if (context_ != nullptr && context_->getDeclKind() == clang::Decl::LinkageSpec) {
248 return context_->getParent();
250 return context_;
253 namespace {
255 bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl *BaseDefinition) {
256 if (BaseDefinition) {
257 auto tc = TypeCheck(BaseDefinition);
258 if (tc.Class("Dialog").GlobalNamespace() || tc.Class("SfxPoolItem").GlobalNamespace()) {
259 return false;
262 return true;
265 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
266 if (!decl)
267 return false;
268 auto tc = TypeCheck(decl);
269 if (tc.Class("Dialog"))
270 return true;
271 if (tc.Class("SfxPoolItem"))
272 return true;
273 if (!decl->hasDefinition()) {
274 return false;
276 if (// not sure what hasAnyDependentBases() does,
277 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
278 !decl->hasAnyDependentBases() &&
279 !decl->forallBases(BaseCheckNotSomethingInterestingSubclass)) {
280 return true;
282 return false;
287 bool isExtraWarnUnusedType(clang::QualType type) {
288 auto const rec = type->getAsCXXRecordDecl();
289 if (rec == nullptr) {
290 return false;
292 auto const tc = TypeCheck(rec);
293 // Check some common non-LO types:
294 if (tc.Class("basic_string").StdNamespace()
295 || tc.Class("deque").StdNamespace()
296 || tc.Class("list").StdNamespace()
297 || tc.Class("map").StdNamespace()
298 || tc.Class("pair").StdNamespace()
299 || tc.Class("queue").StdNamespace()
300 || tc.Class("set").StdNamespace()
301 || tc.Class("stack").StdNamespace()
302 || tc.Class("unordered_map").StdNamespace()
303 || tc.Class("unordered_set").StdNamespace()
304 || tc.Class("vector").StdNamespace())
306 return true;
308 return isDerivedFromSomethingInteresting(rec);
311 namespace {
313 // Make sure Foo and ::Foo are considered equal:
314 bool areSameSugaredType(clang::QualType type1, clang::QualType type2) {
315 auto t1 = type1.getLocalUnqualifiedType();
316 if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(t1)) {
317 t1 = et->getNamedType();
319 auto t2 = type2.getLocalUnqualifiedType();
320 if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(t2)) {
321 t2 = et->getNamedType();
323 return t1 == t2;
326 bool isArithmeticOp(clang::Expr const * expr) {
327 expr = expr->IgnoreParenImpCasts();
328 if (auto const e = llvm::dyn_cast<clang::BinaryOperator>(expr)) {
329 switch (e->getOpcode()) {
330 case clang::BO_Mul:
331 case clang::BO_Div:
332 case clang::BO_Rem:
333 case clang::BO_Add:
334 case clang::BO_Sub:
335 case clang::BO_Shl:
336 case clang::BO_Shr:
337 case clang::BO_And:
338 case clang::BO_Xor:
339 case clang::BO_Or:
340 return true;
341 case clang::BO_Comma:
342 return isArithmeticOp(e->getRHS());
343 default:
344 return false;
347 return llvm::isa<clang::UnaryOperator>(expr)
348 || llvm::isa<clang::AbstractConditionalOperator>(expr);
353 bool isOkToRemoveArithmeticCast(
354 clang::ASTContext & context, clang::QualType t1, clang::QualType t2, const clang::Expr* subExpr)
356 // Don't warn if the types are arithmetic (in the C++ meaning), and: either
357 // at least one is a typedef or decltype (and if both are, they're different),
358 // or the sub-expression involves some operation that is likely to change
359 // types through promotion, or the sub-expression is an integer literal (so
360 // its type generally depends on its value and suffix if any---even with a
361 // suffix like L it could still be either long or long long):
362 if ((t1->isIntegralType(context)
363 || t1->isRealFloatingType())
364 && ((!areSameSugaredType(t1, t2)
365 && (loplugin::TypeCheck(t1).Typedef()
366 || loplugin::TypeCheck(t2).Typedef()
367 || llvm::isa<clang::DecltypeType>(t1) || llvm::isa<clang::DecltypeType>(t2)))
368 || isArithmeticOp(subExpr)
369 || llvm::isa<clang::IntegerLiteral>(subExpr->IgnoreParenImpCasts())))
371 return false;
373 return true;
377 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl *BaseDefinition, void *p) {
378 if (!BaseDefinition)
379 return true;
380 auto const & base = *static_cast<const DeclChecker *>(p);
381 if (base(BaseDefinition)) {
382 return false;
384 return true;
387 bool isDerivedFrom(const clang::CXXRecordDecl *decl, DeclChecker base, bool checkSelf) {
388 if (!decl)
389 return false;
390 if (checkSelf && base(decl))
391 return true;
392 if (!decl->hasDefinition()) {
393 return false;
395 if (!decl->forallBases(
396 [&base](const clang::CXXRecordDecl *BaseDefinition) -> bool
397 { return BaseCheckNotSubclass(BaseDefinition, &base); }))
399 return true;
401 return false;
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */