bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / check.cxx
blobbe1b1f764eb165be809d528c8ce85163336b9978
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 TerminalCheck TypeCheck::Void() const {
68 return TerminalCheck(
69 !type_.isNull()
70 && type_->isSpecificBuiltinType(clang::BuiltinType::Void));
73 TerminalCheck TypeCheck::Char() const {
74 return TerminalCheck(
75 !type_.isNull()
76 && (type_->isSpecificBuiltinType(clang::BuiltinType::Char_S)
77 || type_->isSpecificBuiltinType(clang::BuiltinType::Char_U)));
80 TerminalCheck TypeCheck::AnyBoolean() const {
81 if (type_->isBooleanType()) {
82 return TerminalCheck(true);
84 auto t = type_->getAs<clang::TypedefType>();
85 if (t == nullptr) {
86 return TerminalCheck(false);
88 auto n =t->getDecl()->getName();
89 return TerminalCheck(
90 n == "sal_Bool" || n == "BOOL" || n == "Boolean" || n == "FT_Bool"
91 || n == "FcBool" || n == "GLboolean" || n == "NPBool" || n == "TW_BOOL"
92 || n == "UBool" || n == "boolean" || n == "dbus_bool_t"
93 || n == "gboolean" || n == "hb_bool_t" || n == "jboolean" || n == "my_bool");
96 TypeCheck TypeCheck::LvalueReference() const {
97 if (!type_.isNull()) {
98 auto const t = type_->getAs<clang::LValueReferenceType>();
99 if (t != nullptr) {
100 return TypeCheck(t->getPointeeType());
103 return TypeCheck();
106 TypeCheck TypeCheck::Pointer() const {
107 if (!type_.isNull()) {
108 auto const t = type_->getAs<clang::PointerType>();
109 if (t != nullptr) {
110 return TypeCheck(t->getPointeeType());
113 return TypeCheck();
116 TerminalCheck TypeCheck::Enum() const {
117 if (!type_.isNull()) {
118 auto const t = type_->getAs<clang::EnumType>();
119 if (t != nullptr) {
120 return TerminalCheck(true);
123 return TerminalCheck(false);
126 TypeCheck TypeCheck::Typedef() const {
127 if (!type_.isNull()) {
128 if (auto const t = type_->getAs<clang::TypedefType>()) {
129 return TypeCheck(t->desugar());
132 return TypeCheck();
135 DeclCheck TypeCheck::TemplateSpecializationClass() const {
136 if (!type_.isNull()) {
137 if (auto const t = type_->getAs<clang::TemplateSpecializationType>()) {
138 if (!t->isTypeAlias()) {
139 if (auto const d = llvm::dyn_cast_or_null<clang::ClassTemplateDecl>(
140 t->getTemplateName().getAsTemplateDecl()))
142 return DeclCheck(d->getTemplatedDecl());
147 return DeclCheck();
150 TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const {
151 return
152 (!type_.isNull()
153 && type_->getAs<clang::SubstTemplateTypeParmType>() == nullptr)
154 ? *this : TypeCheck();
157 ContextCheck DeclCheck::Operator(clang::OverloadedOperatorKind op) const {
158 assert(op != clang::OO_None);
159 auto f = llvm::dyn_cast_or_null<clang::FunctionDecl>(decl_);
160 return ContextCheck(
161 f != nullptr && f->getOverloadedOperator() == op
162 ? f->getDeclContext() : nullptr);
165 ContextCheck DeclCheck::MemberFunction() const {
166 auto m = llvm::dyn_cast_or_null<clang::CXXMethodDecl>(decl_);
167 return ContextCheck(m == nullptr ? nullptr : m->getParent());
170 namespace {
172 bool isGlobalNamespace(clang::DeclContext const * context) {
173 assert(context != nullptr);
174 return (context->isLookupContext() ? context : context->getLookupParent())->isTranslationUnit();
179 TerminalCheck ContextCheck::GlobalNamespace() const {
180 return TerminalCheck(context_ != nullptr && isGlobalNamespace(context_));
183 TerminalCheck ContextCheck::StdNamespace() const {
184 return TerminalCheck(
185 context_ != nullptr && context_->isStdNamespace());
188 namespace {
190 bool isStdOrNestedNamespace(clang::DeclContext const * context) {
191 assert(context != nullptr);
192 if (!context->isNamespace()) {
193 return false;
195 if (isGlobalNamespace(context)) {
196 return false;
198 if (context->isStdNamespace()) {
199 return true;
201 return isStdOrNestedNamespace(context->getParent());
206 TerminalCheck ContextCheck::StdOrNestedNamespace() const {
207 return TerminalCheck(context_ != nullptr && isStdOrNestedNamespace(context_));
210 ContextCheck ContextCheck::AnonymousNamespace() const {
211 auto n = llvm::dyn_cast_or_null<clang::NamespaceDecl>(context_);
212 return ContextCheck(
213 n != nullptr && n->isAnonymousNamespace() ? n->getParent() : nullptr);
216 namespace {
218 bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl *BaseDefinition) {
219 if (BaseDefinition) {
220 auto tc = TypeCheck(BaseDefinition);
221 if (tc.Class("Dialog").GlobalNamespace() || tc.Class("SfxPoolItem").GlobalNamespace()) {
222 return false;
225 return true;
228 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
229 if (!decl)
230 return false;
231 auto tc = TypeCheck(decl);
232 if (tc.Class("Dialog"))
233 return true;
234 if (tc.Class("SfxPoolItem"))
235 return true;
236 if (!decl->hasDefinition()) {
237 return false;
239 if (// not sure what hasAnyDependentBases() does,
240 // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
241 !decl->hasAnyDependentBases() &&
242 !decl->forallBases(BaseCheckNotSomethingInterestingSubclass, true)) {
243 return true;
245 return false;
250 bool isExtraWarnUnusedType(clang::QualType type) {
251 auto const rec = type->getAsCXXRecordDecl();
252 if (rec == nullptr) {
253 return false;
255 auto const tc = TypeCheck(rec);
256 // Check some common non-LO types:
257 if (tc.Class("basic_string").StdNamespace()
258 || tc.Class("deque").StdNamespace()
259 || tc.Class("list").StdNamespace()
260 || tc.Class("map").StdNamespace()
261 || tc.Class("pair").StdNamespace()
262 || tc.Class("queue").StdNamespace()
263 || tc.Class("set").StdNamespace()
264 || tc.Class("stack").StdNamespace()
265 || tc.Class("unordered_map").StdNamespace()
266 || tc.Class("unordered_set").StdNamespace()
267 || tc.Class("vector").StdNamespace())
269 return true;
271 return isDerivedFromSomethingInteresting(rec);
274 namespace {
276 bool isArithmeticOp(clang::Expr const * expr) {
277 expr = expr->IgnoreParenImpCasts();
278 if (auto const e = llvm::dyn_cast<clang::BinaryOperator>(expr)) {
279 switch (e->getOpcode()) {
280 case clang::BO_Mul:
281 case clang::BO_Div:
282 case clang::BO_Rem:
283 case clang::BO_Add:
284 case clang::BO_Sub:
285 case clang::BO_Shl:
286 case clang::BO_Shr:
287 case clang::BO_And:
288 case clang::BO_Xor:
289 case clang::BO_Or:
290 return true;
291 case clang::BO_Comma:
292 return isArithmeticOp(e->getRHS());
293 default:
294 return false;
297 return llvm::isa<clang::UnaryOperator>(expr)
298 || llvm::isa<clang::AbstractConditionalOperator>(expr);
303 bool isOkToRemoveArithmeticCast(
304 clang::ASTContext & context, clang::QualType t1, clang::QualType t2, const clang::Expr* subExpr)
306 // Don't warn if the types are arithmetic (in the C++ meaning), and: either
307 // at least one is a typedef or decltype (and if both are, they're different),
308 // or the sub-expression involves some operation that is likely to change
309 // types through promotion, or the sub-expression is an integer literal (so
310 // its type generally depends on its value and suffix if any---even with a
311 // suffix like L it could still be either long or long long):
312 if ((t1->isIntegralType(context)
313 || t1->isRealFloatingType())
314 && ((t1.getLocalUnqualifiedType() != t2.getLocalUnqualifiedType()
315 && (loplugin::TypeCheck(t1).Typedef()
316 || loplugin::TypeCheck(t2).Typedef()
317 || llvm::isa<clang::DecltypeType>(t1) || llvm::isa<clang::DecltypeType>(t2)))
318 || isArithmeticOp(subExpr)
319 || llvm::isa<clang::IntegerLiteral>(subExpr->IgnoreParenImpCasts())))
321 return false;
323 return true;
327 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl *BaseDefinition, void *p) {
328 if (!BaseDefinition)
329 return true;
330 auto const & base = *static_cast<const DeclChecker *>(p);
331 if (base(BaseDefinition)) {
332 return false;
334 return true;
337 bool isDerivedFrom(const clang::CXXRecordDecl *decl, DeclChecker base) {
338 if (!decl)
339 return false;
340 if (base(decl))
341 return true;
342 if (!decl->hasDefinition()) {
343 return false;
345 if (!decl->forallBases(
346 [&base](const clang::CXXRecordDecl *BaseDefinition) -> bool
347 { return BaseCheckNotSubclass(BaseDefinition, &base); },
348 true))
350 return true;
352 return false;
357 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */