bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / check.cxx
blobf2443e44a1f2a193d3f1b31ba8cb8856ff9b9d1c
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>
14 #include "check.hxx"
16 namespace loplugin {
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
23 // checking for
26 TypeCheck TypeCheck::NonConstVolatile() const {
27 return
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
33 // checking for
36 TypeCheck TypeCheck::Const() const {
37 return
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
43 // checking for
46 TypeCheck TypeCheck::Volatile() const {
47 return
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
53 // checking for
56 TypeCheck TypeCheck::ConstVolatile() const {
57 return
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
63 // checking for
66 TerminalCheck TypeCheck::Void() const {
67 return TerminalCheck(
68 !type_.isNull()
69 && type_->isSpecificBuiltinType(clang::BuiltinType::Void));
72 TerminalCheck TypeCheck::Char() const {
73 return TerminalCheck(
74 !type_.isNull()
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>();
84 if (t == nullptr) {
85 return TerminalCheck(false);
87 auto n =t->getDecl()->getName();
88 return TerminalCheck(
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>();
98 if (t != nullptr) {
99 return TypeCheck(t->getPointeeType());
102 return TypeCheck();
105 TypeCheck TypeCheck::Pointer() const {
106 if (!type_.isNull()) {
107 auto const t = type_->getAs<clang::PointerType>();
108 if (t != nullptr) {
109 return TypeCheck(t->getPointeeType());
112 return TypeCheck();
115 TerminalCheck TypeCheck::Enum() const {
116 if (!type_.isNull()) {
117 auto const t = type_->getAs<clang::EnumType>();
118 if (t != nullptr) {
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());
131 return TypeCheck();
134 TypeCheck TypeCheck::NotSubstTemplateTypeParmType() const {
135 return
136 (!type_.isNull()
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_);
144 return ContextCheck(
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(
156 context_ != nullptr
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_);
169 return ContextCheck(
170 n != nullptr && n->isAnonymousNamespace() ? n->getParent() : nullptr);
173 namespace {
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()) {
179 return false;
182 return true;
185 bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
186 if (!decl)
187 return false;
188 auto tc = TypeCheck(decl);
189 if (tc.Class("Dialog"))
190 return true;
191 if (tc.Class("SfxPoolItem"))
192 return true;
193 if (!decl->hasDefinition()) {
194 return false;
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)) {
200 return true;
202 return false;
207 bool isExtraWarnUnusedType(clang::QualType type) {
208 auto const rec = type->getAsCXXRecordDecl();
209 if (rec == nullptr) {
210 return false;
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())
226 return true;
228 return isDerivedFromSomethingInteresting(rec);
231 namespace {
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()) {
237 case clang::BO_Mul:
238 case clang::BO_Div:
239 case clang::BO_Rem:
240 case clang::BO_Add:
241 case clang::BO_Sub:
242 case clang::BO_Shl:
243 case clang::BO_Shr:
244 case clang::BO_And:
245 case clang::BO_Xor:
246 case clang::BO_Or:
247 return true;
248 case clang::BO_Comma:
249 return isArithmeticOp(e->getRHS());
250 default:
251 return false;
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())))
278 return false;
280 return true;
284 static bool BaseCheckNotSubclass(const clang::CXXRecordDecl *BaseDefinition, void *p) {
285 if (!BaseDefinition)
286 return true;
287 auto const & base = *static_cast<const DeclChecker *>(p);
288 if (base(BaseDefinition)) {
289 return false;
291 return true;
294 bool isDerivedFrom(const clang::CXXRecordDecl *decl, DeclChecker base) {
295 if (!decl)
296 return false;
297 if (base(decl))
298 return true;
299 if (!decl->hasDefinition()) {
300 return false;
302 if (!decl->forallBases(
303 [&base](const clang::CXXRecordDecl *BaseDefinition) -> bool
304 { return BaseCheckNotSubclass(BaseDefinition, &base); },
305 true))
307 return true;
309 return false;
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */