summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/shouldreturnbool.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/shouldreturnbool.cxx')
-rw-r--r--compilerplugins/clang/shouldreturnbool.cxx251
1 files changed, 251 insertions, 0 deletions
diff --git a/compilerplugins/clang/shouldreturnbool.cxx b/compilerplugins/clang/shouldreturnbool.cxx
new file mode 100644
index 000000000..bc232b472
--- /dev/null
+++ b/compilerplugins/clang/shouldreturnbool.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <string>
+#include <set>
+#include <iostream>
+
+#include "config_clang.h"
+
+#include "check.hxx"
+#include "compat.hxx"
+#include "plugin.hxx"
+#include "functionaddress.hxx"
+
+/*
+ Look for functions that only return 1 and/or 0, which sometimes indicates that the
+ function should be returning bool.
+
+ Note that is partly a question of taste and code style, which is why this plugin is off by default.
+*/
+
+namespace
+{
+class ShouldReturnBool
+ : public loplugin::FunctionAddress<loplugin::FilteringPlugin<ShouldReturnBool>>
+{
+public:
+ explicit ShouldReturnBool(loplugin::InstantiationData const& data)
+ : FunctionAddress(data)
+ {
+ }
+
+ virtual void run() override
+ {
+ if (!compiler.getLangOpts().CPlusPlus)
+ return;
+ StringRef fn(handler.getMainFileName());
+ // functions used as function pointers
+ if (loplugin::isSamePathname(fn, SRCDIR "/sal/rtl/alloc_cache.cxx"))
+ return;
+ // false +, slightly odd usage, but not wrong
+ if (loplugin::isSamePathname(fn, SRCDIR "/libreofficekit/qa/tilebench/tilebench.cxx"))
+ return;
+ // uses the Unix convention of "non-zero return indicates error"
+ if (loplugin::isSamePathname(fn, SRCDIR "/idlc/source/idlcproduce.cxx"))
+ return;
+ // template magic
+ if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/gdi/bmpfast.cxx"))
+ return;
+ // fine
+ if (loplugin::isSamePathname(fn, SRCDIR "/svl/unx/source/svdde/ddedummy.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/vcl/source/opengl/OpenGLHelper.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/svtools/source/misc/imap2.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/svx/source/dialog/docrecovery.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/lexer.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/hwpfilter/source/grammar.cxx"))
+ return;
+ if (loplugin::isSamePathname(
+ fn, SRCDIR "/connectivity/source/drivers/odbc/ODatabaseMetaDataResultSet.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/dbaccess/source/ui/browser/dsEntriesNoExp.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/lotuswordpro/source/filter/explode.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/filter/source/graphicfilter/ipict/ipict.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/core/data/dptabsrc.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sc/source/ui/docshell/docsh3.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/ui/dlg/masterlayoutdlg.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sd/source/filter/ppt/pptinanimations.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/vcl/unx/generic/app/i18n_im.cxx"))
+ return;
+
+ // callback
+ if (loplugin::isSamePathname(fn, SRCDIR "/sax/source/expatwrap/sax_expat.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/xmlsecurity/source/xmlsec/xmlstreamio.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par2.cxx"))
+ return;
+ if (loplugin::isSamePathname(fn, SRCDIR "/sw/source/filter/ww8/ww8par5.cxx"))
+ return;
+ // SaxWriterHelper::writeSequence a little weird
+ if (loplugin::isSamePathname(fn, SRCDIR "/sax/source/expatwrap/saxwriter.cxx"))
+ return;
+ // main function
+ if (loplugin::isSamePathname(fn, SRCDIR "/xmlsecurity/workben/pdfverify.cxx"))
+ return;
+
+ TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
+
+ for (auto functionDecl : problemFunctions)
+ {
+ auto canonicalDecl = functionDecl->getCanonicalDecl();
+ if (getFunctionsWithAddressTaken().find(canonicalDecl)
+ != getFunctionsWithAddressTaken().end())
+ continue;
+ report(DiagnosticsEngine::Warning,
+ "only returning one or zero is an indication you want to return bool",
+ functionDecl->getBeginLoc())
+ << functionDecl->getSourceRange();
+ if (canonicalDecl->getLocation() != functionDecl->getLocation())
+ {
+ report(DiagnosticsEngine::Note, "canonical function declaration here",
+ canonicalDecl->getBeginLoc())
+ << canonicalDecl->getSourceRange();
+ }
+ }
+ }
+
+ bool TraverseFunctionDecl(FunctionDecl*);
+ bool TraverseCXXMethodDecl(CXXMethodDecl*);
+ bool VisitReturnStmt(ReturnStmt const*);
+
+private:
+ bool mbInsideFunction = false;
+ bool mbFunctionOnlyReturningOneOrZero = false;
+ std::unordered_set<FunctionDecl const*> problemFunctions;
+
+ bool IsInteresting(FunctionDecl const*);
+ void Report(FunctionDecl const*) const;
+ bool isExprOneOrZero(Expr const*) const;
+};
+
+bool ShouldReturnBool::TraverseFunctionDecl(FunctionDecl* functionDecl)
+{
+ bool ret;
+ if (IsInteresting(functionDecl))
+ {
+ mbInsideFunction = true;
+ mbFunctionOnlyReturningOneOrZero = true;
+ ret = FunctionAddress::TraverseFunctionDecl(functionDecl);
+ mbInsideFunction = false;
+ if (mbFunctionOnlyReturningOneOrZero)
+ problemFunctions.insert(functionDecl);
+ }
+ else
+ ret = FunctionAddress::TraverseFunctionDecl(functionDecl);
+ return ret;
+}
+
+bool ShouldReturnBool::TraverseCXXMethodDecl(CXXMethodDecl* methodDecl)
+{
+ bool ret;
+ if (IsInteresting(methodDecl))
+ {
+ mbInsideFunction = true;
+ mbFunctionOnlyReturningOneOrZero = true;
+ ret = FunctionAddress::TraverseCXXMethodDecl(methodDecl);
+ mbInsideFunction = false;
+ if (mbFunctionOnlyReturningOneOrZero)
+ problemFunctions.insert(methodDecl);
+ }
+ else
+ ret = FunctionAddress::TraverseCXXMethodDecl(methodDecl);
+ return ret;
+}
+
+bool ShouldReturnBool::IsInteresting(FunctionDecl const* functionDecl)
+{
+ if (ignoreLocation(functionDecl))
+ return false;
+ // ignore stuff that forms part of the stable URE interface
+ if (isInUnoIncludeFile(functionDecl))
+ return false;
+ if (functionDecl->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
+ return false;
+ if (!functionDecl->isThisDeclarationADefinition())
+ return false;
+ if (functionDecl->isMain())
+ return false;
+ if (functionDecl->isExternC() || functionDecl->isInExternCContext())
+ return false;
+ auto methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
+ if (methodDecl && methodDecl->isVirtual())
+ return false;
+ auto tc = loplugin::TypeCheck(functionDecl->getReturnType());
+ if (tc.AnyBoolean() || tc.Void())
+ return false;
+ auto returnType = functionDecl->getReturnType();
+ if (returnType->isEnumeralType() || !returnType->getUnqualifiedDesugaredType()->isIntegerType())
+ return false;
+ // Ignore functions that contains #ifdef-ery
+ if (containsPreprocessingConditionalInclusion(functionDecl->getSourceRange()))
+ return false;
+
+ // not sure what basegfx is doing here
+ StringRef fileName{ getFilenameOfLocation(functionDecl->getLocation()) };
+ if (loplugin::isSamePathname(fileName, SRCDIR "/include/basegfx/range/basicrange.hxx"))
+ return false;
+ // false +
+ if (loplugin::isSamePathname(fileName, SRCDIR "/include/svl/macitem.hxx"))
+ return false;
+ if (loplugin::isSamePathname(fileName, SRCDIR "/lotuswordpro/source/filter/lwpcharsetmgr.hxx"))
+ return false;
+ if (loplugin::isSamePathname(fileName, SRCDIR "/sc/inc/dptabsrc.hxx"))
+ return false;
+
+ return true;
+}
+
+bool ShouldReturnBool::VisitReturnStmt(const ReturnStmt* returnStmt)
+{
+ if (!mbInsideFunction)
+ return true;
+ if (!returnStmt->getRetValue())
+ return true;
+ if (loplugin::TypeCheck(returnStmt->getRetValue()->getType()).AnyBoolean())
+ return true;
+ if (!isExprOneOrZero(returnStmt->getRetValue()))
+ mbFunctionOnlyReturningOneOrZero = false;
+ return true;
+}
+
+bool ShouldReturnBool::isExprOneOrZero(const Expr* arg) const
+{
+ arg = arg->IgnoreParenCasts();
+ // ignore this, it seems to trigger an infinite recursion
+ if (isa<UnaryExprOrTypeTraitExpr>(arg))
+ {
+ return false;
+ }
+ APSInt x1;
+ if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
+ {
+ return x1 == 1 || x1 == 0;
+ }
+ return false;
+}
+
+loplugin::Plugin::Registration<ShouldReturnBool> X("shouldreturnbool", false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */