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/.
14 #include "config_clang.h"
19 #include "functionaddress.hxx"
22 Look for functions that only return 1 and/or 0, which sometimes indicates that the
23 function should be returning bool.
25 Note that is partly a question of taste and code style, which is why this plugin is off by default.
30 class ShouldReturnBool
31 : public loplugin::FunctionAddress
<loplugin::FilteringPlugin
<ShouldReturnBool
>>
34 explicit ShouldReturnBool(loplugin::InstantiationData
const& data
)
35 : FunctionAddress(data
)
39 virtual void run() override
41 if (!compiler
.getLangOpts().CPlusPlus
)
43 StringRef
fn(handler
.getMainFileName());
44 // functions used as function pointers
45 if (loplugin::isSamePathname(fn
, SRCDIR
"/sal/rtl/alloc_cache.cxx"))
47 // false +, slightly odd usage, but not wrong
48 if (loplugin::isSamePathname(fn
, SRCDIR
"/libreofficekit/qa/tilebench/tilebench.cxx"))
51 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/gdi/bmpfast.cxx"))
54 if (loplugin::isSamePathname(fn
, SRCDIR
"/svl/unx/source/svdde/ddedummy.cxx"))
56 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/source/opengl/OpenGLHelper.cxx"))
58 if (loplugin::isSamePathname(fn
, SRCDIR
"/svtools/source/misc/imap2.cxx"))
60 if (loplugin::isSamePathname(fn
, SRCDIR
"/svx/source/dialog/docrecovery.cxx"))
62 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/lexer.cxx"))
64 if (loplugin::isSamePathname(fn
, SRCDIR
"/hwpfilter/source/grammar.cxx"))
66 if (loplugin::isSamePathname(
67 fn
, SRCDIR
"/connectivity/source/drivers/odbc/ODatabaseMetaDataResultSet.cxx"))
69 if (loplugin::isSamePathname(fn
, SRCDIR
"/dbaccess/source/ui/browser/dsEntriesNoExp.cxx"))
71 if (loplugin::isSamePathname(fn
, SRCDIR
"/lotuswordpro/source/filter/explode.cxx"))
73 if (loplugin::isSamePathname(fn
, SRCDIR
"/filter/source/graphicfilter/ipict/ipict.cxx"))
75 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/core/data/dptabsrc.cxx"))
77 if (loplugin::isSamePathname(fn
, SRCDIR
"/sc/source/ui/docshell/docsh3.cxx"))
79 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/ui/dlg/masterlayoutdlg.cxx"))
81 if (loplugin::isSamePathname(fn
, SRCDIR
"/sd/source/filter/ppt/pptinanimations.cxx"))
83 if (loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/generic/app/i18n_im.cxx"))
87 if (loplugin::isSamePathname(fn
, SRCDIR
"/sax/source/expatwrap/sax_expat.cxx"))
89 if (loplugin::isSamePathname(fn
, SRCDIR
"/xmlsecurity/source/xmlsec/xmlstreamio.cxx"))
91 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par.cxx"))
93 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par2.cxx"))
95 if (loplugin::isSamePathname(fn
, SRCDIR
"/sw/source/filter/ww8/ww8par5.cxx"))
97 // SaxWriterHelper::writeSequence a little weird
98 if (loplugin::isSamePathname(fn
, SRCDIR
"/sax/source/expatwrap/saxwriter.cxx"))
101 if (loplugin::isSamePathname(fn
, SRCDIR
"/xmlsecurity/workben/pdfverify.cxx"))
104 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
106 for (auto functionDecl
: problemFunctions
)
108 auto canonicalDecl
= functionDecl
->getCanonicalDecl();
109 if (getFunctionsWithAddressTaken().find(canonicalDecl
)
110 != getFunctionsWithAddressTaken().end())
112 report(DiagnosticsEngine::Warning
,
113 "only returning one or zero is an indication you want to return bool",
114 functionDecl
->getBeginLoc())
115 << functionDecl
->getSourceRange();
116 if (canonicalDecl
->getLocation() != functionDecl
->getLocation())
118 report(DiagnosticsEngine::Note
, "canonical function declaration here",
119 canonicalDecl
->getBeginLoc())
120 << canonicalDecl
->getSourceRange();
125 bool TraverseFunctionDecl(FunctionDecl
*);
126 bool TraverseCXXMethodDecl(CXXMethodDecl
*);
127 bool VisitReturnStmt(ReturnStmt
const*);
130 bool mbInsideFunction
= false;
131 bool mbFunctionOnlyReturningOneOrZero
= false;
132 std::unordered_set
<FunctionDecl
const*> problemFunctions
;
134 bool IsInteresting(FunctionDecl
const*);
135 void Report(FunctionDecl
const*) const;
136 bool isExprOneOrZero(Expr
const*) const;
139 bool ShouldReturnBool::TraverseFunctionDecl(FunctionDecl
* functionDecl
)
142 if (IsInteresting(functionDecl
))
144 mbInsideFunction
= true;
145 mbFunctionOnlyReturningOneOrZero
= true;
146 ret
= FunctionAddress::TraverseFunctionDecl(functionDecl
);
147 mbInsideFunction
= false;
148 if (mbFunctionOnlyReturningOneOrZero
)
149 problemFunctions
.insert(functionDecl
);
152 ret
= FunctionAddress::TraverseFunctionDecl(functionDecl
);
156 bool ShouldReturnBool::TraverseCXXMethodDecl(CXXMethodDecl
* methodDecl
)
159 if (IsInteresting(methodDecl
))
161 mbInsideFunction
= true;
162 mbFunctionOnlyReturningOneOrZero
= true;
163 ret
= FunctionAddress::TraverseCXXMethodDecl(methodDecl
);
164 mbInsideFunction
= false;
165 if (mbFunctionOnlyReturningOneOrZero
)
166 problemFunctions
.insert(methodDecl
);
169 ret
= FunctionAddress::TraverseCXXMethodDecl(methodDecl
);
173 bool ShouldReturnBool::IsInteresting(FunctionDecl
const* functionDecl
)
175 if (ignoreLocation(functionDecl
))
177 // ignore stuff that forms part of the stable URE interface
178 if (isInUnoIncludeFile(functionDecl
))
180 if (functionDecl
->getTemplatedKind() != FunctionDecl::TK_NonTemplate
)
182 if (!functionDecl
->isThisDeclarationADefinition())
184 if (functionDecl
->isMain())
186 if (functionDecl
->isExternC() || functionDecl
->isInExternCContext())
188 auto methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
189 if (methodDecl
&& methodDecl
->isVirtual())
191 auto tc
= loplugin::TypeCheck(functionDecl
->getReturnType());
192 if (tc
.AnyBoolean() || tc
.Void())
194 auto returnType
= functionDecl
->getReturnType();
195 if (returnType
->isEnumeralType() || !returnType
->getUnqualifiedDesugaredType()->isIntegerType())
197 // Ignore functions that contains #ifdef-ery
198 if (containsPreprocessingConditionalInclusion(functionDecl
->getSourceRange()))
201 // not sure what basegfx is doing here
202 StringRef fileName
{ getFilenameOfLocation(functionDecl
->getLocation()) };
203 if (loplugin::isSamePathname(fileName
, SRCDIR
"/include/basegfx/range/basicrange.hxx"))
206 if (loplugin::isSamePathname(fileName
, SRCDIR
"/include/svl/macitem.hxx"))
208 if (loplugin::isSamePathname(fileName
, SRCDIR
"/lotuswordpro/source/filter/lwpcharsetmgr.hxx"))
210 if (loplugin::isSamePathname(fileName
, SRCDIR
"/sc/inc/dptabsrc.hxx"))
216 bool ShouldReturnBool::VisitReturnStmt(const ReturnStmt
* returnStmt
)
218 if (!mbInsideFunction
)
220 if (!returnStmt
->getRetValue())
222 if (loplugin::TypeCheck(returnStmt
->getRetValue()->getType()).AnyBoolean())
224 if (!isExprOneOrZero(returnStmt
->getRetValue()))
225 mbFunctionOnlyReturningOneOrZero
= false;
229 bool ShouldReturnBool::isExprOneOrZero(const Expr
* arg
) const
231 arg
= arg
->IgnoreParenCasts();
232 // ignore this, it seems to trigger an infinite recursion
233 if (isa
<UnaryExprOrTypeTraitExpr
>(arg
))
238 if (compat::EvaluateAsInt(arg
, x1
, compiler
.getASTContext()))
240 return x1
== 1 || x1
== 0;
245 loplugin::Plugin::Registration
<ShouldReturnBool
> X("shouldreturnbool", false);
248 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */