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>
18 TypeCheck
TypeCheck::NonConst() const {
19 return !type_
.isNull() && !type_
.isConstQualified()
20 ? *this : TypeCheck();
21 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
22 // may look tempting, but could remove sugar we might be interested in
26 TypeCheck
TypeCheck::NonConstVolatile() const {
28 (!type_
.isNull() && !type_
.isConstQualified()
29 && !type_
.isVolatileQualified())
30 ? *this : TypeCheck();
31 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
32 // may look tempting, but could remove sugar we might be interested in
36 TypeCheck
TypeCheck::Const() const {
38 (!type_
.isNull() && type_
.isConstQualified()
39 && !type_
.isVolatileQualified())
40 ? *this : TypeCheck();
41 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
42 // may look tempting, but could remove sugar we might be interested in
46 TypeCheck
TypeCheck::Volatile() const {
48 (!type_
.isNull() && !type_
.isConstQualified()
49 && type_
.isVolatileQualified())
50 ? *this : TypeCheck();
51 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
52 // may look tempting, but could remove sugar we might be interested in
56 TypeCheck
TypeCheck::ConstVolatile() const {
58 (!type_
.isNull() && type_
.isConstQualified()
59 && type_
.isVolatileQualified())
60 ? *this : TypeCheck();
61 // returning TypeCheck(type_.getUnqualifiedType()) instead of *this
62 // may look tempting, but could remove sugar we might be interested in
66 TerminalCheck
TypeCheck::Void() const {
69 && type_
->isSpecificBuiltinType(clang::BuiltinType::Void
));
72 TerminalCheck
TypeCheck::Char() const {
75 && (type_
->isSpecificBuiltinType(clang::BuiltinType::Char_S
)
76 || type_
->isSpecificBuiltinType(clang::BuiltinType::Char_U
)));
79 TerminalCheck
TypeCheck::AnyBoolean() const {
80 if (type_
->isBooleanType()) {
81 return TerminalCheck(true);
83 auto t
= type_
->getAs
<clang::TypedefType
>();
85 return TerminalCheck(false);
87 auto n
=t
->getDecl()->getName();
89 n
== "sal_Bool" || n
== "BOOL" || n
== "Boolean" || n
== "FT_Bool"
90 || n
== "FcBool" || n
== "GLboolean" || n
== "NPBool" || n
== "TW_BOOL"
91 || n
== "UBool" || n
== "boolean" || n
== "dbus_bool_t"
92 || n
== "gboolean" || n
== "hb_bool_t" || n
== "jboolean" || n
== "my_bool");
95 TypeCheck
TypeCheck::LvalueReference() const {
96 if (!type_
.isNull()) {
97 auto const t
= type_
->getAs
<clang::LValueReferenceType
>();
99 return TypeCheck(t
->getPointeeType());
105 TypeCheck
TypeCheck::Pointer() const {
106 if (!type_
.isNull()) {
107 auto const t
= type_
->getAs
<clang::PointerType
>();
109 return TypeCheck(t
->getPointeeType());
115 TerminalCheck
TypeCheck::Enum() const {
116 if (!type_
.isNull()) {
117 auto const t
= type_
->getAs
<clang::EnumType
>();
119 return TerminalCheck(true);
122 return TerminalCheck(false);
125 TypeCheck
TypeCheck::Typedef() const {
126 if (!type_
.isNull()) {
127 if (auto const t
= type_
->getAs
<clang::TypedefType
>()) {
128 return TypeCheck(t
->desugar());
134 TypeCheck
TypeCheck::NotSubstTemplateTypeParmType() const {
137 && type_
->getAs
<clang::SubstTemplateTypeParmType
>() == nullptr)
138 ? *this : TypeCheck();
141 ContextCheck
DeclCheck::Operator(clang::OverloadedOperatorKind op
) const {
142 assert(op
!= clang::OO_None
);
143 auto f
= llvm::dyn_cast_or_null
<clang::FunctionDecl
>(decl_
);
145 f
!= nullptr && f
->getOverloadedOperator() == op
146 ? f
->getDeclContext() : nullptr);
149 ContextCheck
DeclCheck::MemberFunction() const {
150 auto m
= llvm::dyn_cast_or_null
<clang::CXXMethodDecl
>(decl_
);
151 return ContextCheck(m
== nullptr ? nullptr : m
->getParent());
154 TerminalCheck
ContextCheck::GlobalNamespace() const {
155 return TerminalCheck(
157 && ((context_
->isLookupContext()
158 ? context_
: context_
->getLookupParent())
159 ->isTranslationUnit()));
162 TerminalCheck
ContextCheck::StdNamespace() const {
163 return TerminalCheck(
164 context_
!= nullptr && context_
->isStdNamespace());
167 ContextCheck
ContextCheck::AnonymousNamespace() const {
168 auto n
= llvm::dyn_cast_or_null
<clang::NamespaceDecl
>(context_
);
170 n
!= nullptr && n
->isAnonymousNamespace() ? n
->getParent() : nullptr);
175 bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl
*BaseDefinition
) {
176 if (BaseDefinition
) {
177 auto tc
= TypeCheck(BaseDefinition
);
178 if (tc
.Class("Dialog").GlobalNamespace() || tc
.Class("SfxPoolItem").GlobalNamespace()) {
185 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl
*decl
) {
188 auto tc
= TypeCheck(decl
);
189 if (tc
.Class("Dialog"))
191 if (tc
.Class("SfxPoolItem"))
193 if (!decl
->hasDefinition()) {
196 if (// not sure what hasAnyDependentBases() does,
197 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
198 !decl
->hasAnyDependentBases() &&
199 !decl
->forallBases(BaseCheckNotSomethingInterestingSubclass
, true)) {
207 bool isExtraWarnUnusedType(clang::QualType type
) {
208 auto const rec
= type
->getAsCXXRecordDecl();
209 if (rec
== nullptr) {
212 auto const tc
= TypeCheck(rec
);
213 // Check some common non-LO types:
214 if (tc
.Class("basic_string").StdNamespace()
215 || tc
.Class("deque").StdNamespace()
216 || tc
.Class("list").StdNamespace()
217 || tc
.Class("map").StdNamespace()
218 || tc
.Class("pair").StdNamespace()
219 || tc
.Class("queue").StdNamespace()
220 || tc
.Class("set").StdNamespace()
221 || tc
.Class("stack").StdNamespace()
222 || tc
.Class("unordered_map").StdNamespace()
223 || tc
.Class("unordered_set").StdNamespace()
224 || tc
.Class("vector").StdNamespace())
228 return isDerivedFromSomethingInteresting(rec
);
233 bool isArithmeticOp(clang::Expr
const * expr
) {
234 expr
= expr
->IgnoreParenImpCasts();
235 if (auto const e
= llvm::dyn_cast
<clang::BinaryOperator
>(expr
)) {
236 switch (e
->getOpcode()) {
248 case clang::BO_Comma
:
249 return isArithmeticOp(e
->getRHS());
254 return llvm::isa
<clang::UnaryOperator
>(expr
)
255 || llvm::isa
<clang::AbstractConditionalOperator
>(expr
);
260 bool isOkToRemoveArithmeticCast(
261 clang::ASTContext
& context
, clang::QualType t1
, clang::QualType t2
, const clang::Expr
* subExpr
)
263 // Don't warn if the types are arithmetic (in the C++ meaning), and: either
264 // at least one is a typedef or decltype (and if both are, they're different),
265 // or the sub-expression involves some operation that is likely to change
266 // types through promotion, or the sub-expression is an integer literal (so
267 // its type generally depends on its value and suffix if any---even with a
268 // suffix like L it could still be either long or long long):
269 if ((t1
->isIntegralType(context
)
270 || t1
->isRealFloatingType())
271 && ((t1
.getLocalUnqualifiedType() != t2
.getLocalUnqualifiedType()
272 && (loplugin::TypeCheck(t1
).Typedef()
273 || loplugin::TypeCheck(t2
).Typedef()
274 || llvm::isa
<clang::DecltypeType
>(t1
) || llvm::isa
<clang::DecltypeType
>(t2
)))
275 || isArithmeticOp(subExpr
)
276 || llvm::isa
<clang::IntegerLiteral
>(subExpr
->IgnoreParenImpCasts())))
284 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl
*BaseDefinition
, void *p
) {
287 auto const & base
= *static_cast<const DeclChecker
*>(p
);
288 if (base(BaseDefinition
)) {
294 bool isDerivedFrom(const clang::CXXRecordDecl
*decl
, DeclChecker base
) {
299 if (!decl
->hasDefinition()) {
302 if (!decl
->forallBases(
303 [&base
](const clang::CXXRecordDecl
*BaseDefinition
) -> bool
304 { return BaseCheckNotSubclass(BaseDefinition
, &base
); },
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */