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 TerminalCheck
TypeCheck::Enum() const {
137 if (!type_
.isNull()) {
138 auto const t
= type_
->getAs
<clang::EnumType
>();
140 return TerminalCheck(true);
143 return TerminalCheck(false);
146 TypeCheck
TypeCheck::Typedef() const {
147 if (!type_
.isNull()) {
148 if (auto const t
= type_
->getAs
<clang::TypedefType
>()) {
149 return TypeCheck(t
->desugar());
155 DeclCheck
TypeCheck::TemplateSpecializationClass() const {
156 if (!type_
.isNull()) {
157 if (auto const t
= type_
->getAs
<clang::TemplateSpecializationType
>()) {
158 if (!t
->isTypeAlias()) {
159 if (auto const d
= llvm::dyn_cast_or_null
<clang::ClassTemplateDecl
>(
160 t
->getTemplateName().getAsTemplateDecl()))
162 return DeclCheck(d
->getTemplatedDecl());
170 TypeCheck
TypeCheck::NotSubstTemplateTypeParmType() const {
173 && type_
->getAs
<clang::SubstTemplateTypeParmType
>() == nullptr)
174 ? *this : TypeCheck();
177 ContextCheck
DeclCheck::Operator(clang::OverloadedOperatorKind op
) const {
178 assert(op
!= clang::OO_None
);
179 auto f
= llvm::dyn_cast_or_null
<clang::FunctionDecl
>(decl_
);
181 f
!= nullptr && f
->getOverloadedOperator() == op
182 ? f
->getDeclContext() : nullptr);
185 ContextCheck
DeclCheck::MemberFunction() const {
186 auto m
= llvm::dyn_cast_or_null
<clang::CXXMethodDecl
>(decl_
);
187 return ContextCheck(m
== nullptr ? nullptr : m
->getParent());
192 bool isGlobalNamespace(clang::DeclContext
const * context
) {
193 assert(context
!= nullptr);
194 return context
->getEnclosingNamespaceContext()->isTranslationUnit();
199 TerminalCheck
ContextCheck::GlobalNamespace() const {
200 return TerminalCheck(context_
!= nullptr && isGlobalNamespace(context_
));
203 TerminalCheck
ContextCheck::StdNamespace() const {
204 return TerminalCheck(
205 context_
!= nullptr && lookThroughLinkageSpec()->isStdNamespace());
210 bool isStdOrNestedNamespace(clang::DeclContext
const * context
) {
211 assert(context
!= nullptr);
212 if (!context
->isNamespace()) {
215 if (isGlobalNamespace(context
)) {
218 if (context
->isStdNamespace()) {
221 return isStdOrNestedNamespace(context
->getParent());
226 TerminalCheck
ContextCheck::StdOrNestedNamespace() const {
227 return TerminalCheck(context_
!= nullptr && isStdOrNestedNamespace(lookThroughLinkageSpec()));
230 ContextCheck
ContextCheck::AnonymousNamespace() const {
231 auto n
= llvm::dyn_cast_or_null
<clang::NamespaceDecl
>(lookThroughLinkageSpec());
233 n
!= nullptr && n
->isAnonymousNamespace() ? n
->getParent() : nullptr);
236 clang::DeclContext
const * ContextCheck::lookThroughLinkageSpec() const {
237 if (context_
!= nullptr && context_
->getDeclKind() == clang::Decl::LinkageSpec
) {
238 return context_
->getParent();
245 bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl
*BaseDefinition
) {
246 if (BaseDefinition
) {
247 auto tc
= TypeCheck(BaseDefinition
);
248 if (tc
.Class("Dialog").GlobalNamespace() || tc
.Class("SfxPoolItem").GlobalNamespace()) {
255 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl
*decl
) {
258 auto tc
= TypeCheck(decl
);
259 if (tc
.Class("Dialog"))
261 if (tc
.Class("SfxPoolItem"))
263 if (!decl
->hasDefinition()) {
266 if (// not sure what hasAnyDependentBases() does,
267 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
268 !decl
->hasAnyDependentBases() &&
269 !decl
->forallBases(BaseCheckNotSomethingInterestingSubclass
)) {
277 bool isExtraWarnUnusedType(clang::QualType type
) {
278 auto const rec
= type
->getAsCXXRecordDecl();
279 if (rec
== nullptr) {
282 auto const tc
= TypeCheck(rec
);
283 // Check some common non-LO types:
284 if (tc
.Class("basic_string").StdNamespace()
285 || tc
.Class("deque").StdNamespace()
286 || tc
.Class("list").StdNamespace()
287 || tc
.Class("map").StdNamespace()
288 || tc
.Class("pair").StdNamespace()
289 || tc
.Class("queue").StdNamespace()
290 || tc
.Class("set").StdNamespace()
291 || tc
.Class("stack").StdNamespace()
292 || tc
.Class("unordered_map").StdNamespace()
293 || tc
.Class("unordered_set").StdNamespace()
294 || tc
.Class("vector").StdNamespace())
298 return isDerivedFromSomethingInteresting(rec
);
303 // Make sure Foo and ::Foo are considered equal:
304 bool areSameSugaredType(clang::QualType type1
, clang::QualType type2
) {
305 auto t1
= type1
.getLocalUnqualifiedType();
306 if (auto const et
= llvm::dyn_cast
<clang::ElaboratedType
>(t1
)) {
307 t1
= et
->getNamedType();
309 auto t2
= type2
.getLocalUnqualifiedType();
310 if (auto const et
= llvm::dyn_cast
<clang::ElaboratedType
>(t2
)) {
311 t2
= et
->getNamedType();
316 bool isArithmeticOp(clang::Expr
const * expr
) {
317 expr
= expr
->IgnoreParenImpCasts();
318 if (auto const e
= llvm::dyn_cast
<clang::BinaryOperator
>(expr
)) {
319 switch (e
->getOpcode()) {
331 case clang::BO_Comma
:
332 return isArithmeticOp(e
->getRHS());
337 return llvm::isa
<clang::UnaryOperator
>(expr
)
338 || llvm::isa
<clang::AbstractConditionalOperator
>(expr
);
343 bool isOkToRemoveArithmeticCast(
344 clang::ASTContext
& context
, clang::QualType t1
, clang::QualType t2
, const clang::Expr
* subExpr
)
346 // Don't warn if the types are arithmetic (in the C++ meaning), and: either
347 // at least one is a typedef or decltype (and if both are, they're different),
348 // or the sub-expression involves some operation that is likely to change
349 // types through promotion, or the sub-expression is an integer literal (so
350 // its type generally depends on its value and suffix if any---even with a
351 // suffix like L it could still be either long or long long):
352 if ((t1
->isIntegralType(context
)
353 || t1
->isRealFloatingType())
354 && ((!areSameSugaredType(t1
, t2
)
355 && (loplugin::TypeCheck(t1
).Typedef()
356 || loplugin::TypeCheck(t2
).Typedef()
357 || llvm::isa
<clang::DecltypeType
>(t1
) || llvm::isa
<clang::DecltypeType
>(t2
)))
358 || isArithmeticOp(subExpr
)
359 || llvm::isa
<clang::IntegerLiteral
>(subExpr
->IgnoreParenImpCasts())))
367 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl
*BaseDefinition
, void *p
) {
370 auto const & base
= *static_cast<const DeclChecker
*>(p
);
371 if (base(BaseDefinition
)) {
377 bool isDerivedFrom(const clang::CXXRecordDecl
*decl
, DeclChecker base
, bool checkSelf
) {
380 if (checkSelf
&& base(decl
))
382 if (!decl
->hasDefinition()) {
385 if (!decl
->forallBases(
386 [&base
](const clang::CXXRecordDecl
*BaseDefinition
) -> bool
387 { return BaseCheckNotSubclass(BaseDefinition
, &base
); }))
396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */