1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
12 #include <clang/AST/DeclCXX.h>
13 #include <clang/AST/DeclTemplate.h>
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
27 TypeCheck
TypeCheck::NonConstVolatile() const {
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
37 TypeCheck
TypeCheck::Const() const {
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
47 TypeCheck
TypeCheck::Volatile() const {
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
57 TypeCheck
TypeCheck::ConstVolatile() const {
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
67 TypeCheck
TypeCheck::ConstNonVolatile() const {
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
77 TerminalCheck
TypeCheck::Void() const {
80 && type_
->isSpecificBuiltinType(clang::BuiltinType::Void
));
83 TerminalCheck
TypeCheck::Char() const {
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
>();
96 return TerminalCheck(false);
98 auto n
=t
->getDecl()->getName();
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
>();
110 return TypeCheck(t
->getPointeeType());
116 TypeCheck
TypeCheck::RvalueReference() const {
117 if (!type_
.isNull()) {
118 auto const t
= type_
->getAs
<clang::RValueReferenceType
>();
120 return TypeCheck(t
->getPointeeType());
126 TypeCheck
TypeCheck::Pointer() const {
127 if (!type_
.isNull()) {
128 auto const t
= type_
->getAs
<clang::PointerType
>();
130 return TypeCheck(t
->getPointeeType());
136 TypeCheck
TypeCheck::MemberPointerOf() const {
137 if (!type_
.isNull()) {
138 auto const t
= type_
->getAs
<clang::MemberPointerType
>();
140 return TypeCheck(t
->getClass());
146 TerminalCheck
TypeCheck::Enum() const {
147 if (!type_
.isNull()) {
148 auto const t
= type_
->getAs
<clang::EnumType
>();
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());
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());
180 TypeCheck
TypeCheck::NotSubstTemplateTypeParmType() const {
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_
);
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());
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());
220 bool isStdOrNestedNamespace(clang::DeclContext
const * context
) {
221 assert(context
!= nullptr);
222 if (!context
->isNamespace()) {
225 if (isGlobalNamespace(context
)) {
228 if (context
->isStdNamespace()) {
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());
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();
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()) {
265 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl
*decl
) {
268 auto tc
= TypeCheck(decl
);
269 if (tc
.Class("Dialog"))
271 if (tc
.Class("SfxPoolItem"))
273 if (!decl
->hasDefinition()) {
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
)) {
287 bool isExtraWarnUnusedType(clang::QualType type
) {
288 auto const rec
= type
->getAsCXXRecordDecl();
289 if (rec
== nullptr) {
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())
308 return isDerivedFromSomethingInteresting(rec
);
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();
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()) {
341 case clang::BO_Comma
:
342 return isArithmeticOp(e
->getRHS());
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())))
377 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl
*BaseDefinition
, void *p
) {
380 auto const & base
= *static_cast<const DeclChecker
*>(p
);
381 if (base(BaseDefinition
)) {
387 bool isDerivedFrom(const clang::CXXRecordDecl
*decl
, DeclChecker base
, bool checkSelf
) {
390 if (checkSelf
&& base(decl
))
392 if (!decl
->hasDefinition()) {
395 if (!decl
->forallBases(
396 [&base
](const clang::CXXRecordDecl
*BaseDefinition
) -> bool
397 { return BaseCheckNotSubclass(BaseDefinition
, &base
); }))
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */