summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/empty.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/empty.cxx')
-rw-r--r--compilerplugins/clang/empty.cxx167
1 files changed, 167 insertions, 0 deletions
diff --git a/compilerplugins/clang/empty.cxx b/compilerplugins/clang/empty.cxx
new file mode 100644
index 0000000000..d5129d29dd
--- /dev/null
+++ b/compilerplugins/clang/empty.cxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#ifndef LO_CLANG_SHARED_PLUGINS
+
+#include <cassert>
+
+#include "check.hxx"
+#include "plugin.hxx"
+
+// Warn about checks whether a container is empty done via an (expensive) call to obtain the
+// container's size. For now only handles cases involving strlen.
+
+namespace
+{
+BinaryOperatorKind reverse(BinaryOperatorKind op)
+{
+ switch (op)
+ {
+ case BO_LT:
+ return BO_GE;
+ case BO_GT:
+ return BO_LE;
+ case BO_LE:
+ return BO_GT;
+ case BO_GE:
+ return BO_LT;
+ case BO_EQ:
+ case BO_NE:
+ return op;
+ default:
+ abort();
+ }
+}
+
+class Empty : public loplugin::FilteringPlugin<Empty>
+{
+public:
+ explicit Empty(loplugin::InstantiationData const& data)
+ : FilteringPlugin(data)
+ {
+ }
+
+ bool VisitBinaryOperator(BinaryOperator const* expr)
+ {
+ if (expr->isRelationalOp() || expr->isEqualityOp())
+ {
+ visitComparison(expr);
+ }
+ return true;
+ }
+
+private:
+ void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
+
+ void visitComparison(BinaryOperator const* expr, CallExpr const* lhs, Expr const* rhs,
+ BinaryOperatorKind op)
+ {
+ auto const fdecl = lhs->getDirectCallee();
+ if (fdecl == nullptr)
+ {
+ return;
+ }
+ loplugin::DeclCheck dc(fdecl);
+ if (!(dc.Function("strlen").StdNamespace() || dc.Function("strlen").GlobalNamespace()))
+ {
+ return;
+ }
+ if (rhs->isValueDependent())
+ {
+ return;
+ }
+ auto const val = rhs->getIntegerConstantExpr(compiler.getASTContext());
+ if (!val)
+ {
+ return;
+ }
+ switch (op)
+ {
+ case BO_LT:
+ if (val->getExtValue() == 1)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) < 1' with 'e[0] == '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ case BO_GT:
+ if (val->getExtValue() == 0)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) > 0' with 'e[0] != '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ case BO_LE:
+ if (val->getExtValue() == 0)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) <= 0' with 'e[0] == '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ case BO_GE:
+ if (val->getExtValue() == 1)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) >= 1' with 'e[0] != '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ case BO_EQ:
+ if (val->getExtValue() == 0)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) == 0' with 'e[0] == '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ case BO_NE:
+ if (val->getExtValue() == 0)
+ {
+ report(DiagnosticsEngine::Warning,
+ "replace a comparison like 'strlen(e) != 0' with 'e[0] != '\\0''",
+ expr->getExprLoc())
+ << expr->getSourceRange();
+ }
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ void visitComparison(BinaryOperator const* expr)
+ {
+ if (ignoreLocation(expr))
+ {
+ return;
+ }
+ if (auto const call = dyn_cast<CallExpr>(expr->getLHS()->IgnoreParenImpCasts()))
+ {
+ visitComparison(expr, call, expr->getRHS(), expr->getOpcode());
+ }
+ else if (auto const call = dyn_cast<CallExpr>(expr->getRHS()->IgnoreParenImpCasts()))
+ {
+ visitComparison(expr, call, expr->getLHS(), reverse(expr->getOpcode()));
+ }
+ }
+};
+
+loplugin::Plugin::Registration<Empty> emptyRegistration("empty");
+}
+
+#endif // LO_CLANG_SHARED_PLUGINS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */