Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / check.cxx
blob4ff081b6923e55ca3b5db66b5a0ffdadcb3a58b9
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 TerminalCheck TypeCheck::Enum() const {
137 if (!type_.isNull()) {
138 auto const t = type_->getAs<clang::EnumType>();
139 if (t != nullptr) {
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());
152 return TypeCheck();
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());
167 return DeclCheck();
170 TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const {
171 return
172 (!type_.isNull()
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_);
180 return ContextCheck(
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());
190 namespace {
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());
208 namespace {
210 bool isStdOrNestedNamespace(clang::DeclContext const * context) {
211 assert(context != nullptr);
212 if (!context->isNamespace()) {
213 return false;
215 if (isGlobalNamespace(context)) {
216 return false;
218 if (context->isStdNamespace()) {
219 return true;
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());
232 return ContextCheck(
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();
240 return context_;
243 namespace {
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()) {
249 return false;
252 return true;
255 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
256 if (!decl)
257 return false;
258 auto tc = TypeCheck(decl);
259 if (tc.Class("Dialog"))
260 return true;
261 if (tc.Class("SfxPoolItem"))
262 return true;
263 if (!decl->hasDefinition()) {
264 return false;
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)) {
270 return true;
272 return false;
277 bool isExtraWarnUnusedType(clang::QualType type) {
278 auto const rec = type->getAsCXXRecordDecl();
279 if (rec == nullptr) {
280 return false;
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())
296 return true;
298 return isDerivedFromSomethingInteresting(rec);
301 namespace {
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();
313 return t1 == t2;
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()) {
320 case clang::BO_Mul:
321 case clang::BO_Div:
322 case clang::BO_Rem:
323 case clang::BO_Add:
324 case clang::BO_Sub:
325 case clang::BO_Shl:
326 case clang::BO_Shr:
327 case clang::BO_And:
328 case clang::BO_Xor:
329 case clang::BO_Or:
330 return true;
331 case clang::BO_Comma:
332 return isArithmeticOp(e->getRHS());
333 default:
334 return false;
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())))
361 return false;
363 return true;
367 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl *BaseDefinition, void *p) {
368 if (!BaseDefinition)
369 return true;
370 auto const & base = *static_cast<const DeclChecker *>(p);
371 if (base(BaseDefinition)) {
372 return false;
374 return true;
377 bool isDerivedFrom(const clang::CXXRecordDecl *decl, DeclChecker base, bool checkSelf) {
378 if (!decl)
379 return false;
380 if (checkSelf && base(decl))
381 return true;
382 if (!decl->hasDefinition()) {
383 return false;
385 if (!decl->forallBases(
386 [&base](const clang::CXXRecordDecl *BaseDefinition) -> bool
387 { return BaseCheckNotSubclass(BaseDefinition, &base); }))
389 return true;
391 return false;
396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */