summaryrefslogtreecommitdiff
path: root/compilerplugins
diff options
context:
space:
mode:
authorNoel Grandin <noel@peralex.com>2015-05-27 16:33:44 +0200
committerNoel Grandin <noel@peralex.com>2015-05-28 12:49:54 +0200
commitd5129a9dd68978f9eccdd4597b5b6834557c422a (patch)
treedf43250172f784f3048ce42ce1c3410d1d449c1e /compilerplugins
parentf3331f7694e74f349375c223ce7ed84838e92d89 (diff)
new clang plugin: loopvartoosmall
Idea from bubli - look for loops where the index variable is of such size that it cannot cover the range revealed by examining the length part of the condition. So far, I have only run the plugin up till the VCL module. Also the plugin deliberately excludes anything more complicated than a straightforward incrementing for loop. Change-Id: Ifced18b01c03ea537c64168465ce0b8287a42015
Diffstat (limited to 'compilerplugins')
-rw-r--r--compilerplugins/clang/loopvartoosmall.cxx119
1 files changed, 119 insertions, 0 deletions
diff --git a/compilerplugins/clang/loopvartoosmall.cxx b/compilerplugins/clang/loopvartoosmall.cxx
new file mode 100644
index 000000000000..9cfa9f272444
--- /dev/null
+++ b/compilerplugins/clang/loopvartoosmall.cxx
@@ -0,0 +1,119 @@
+/* -*- 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 <iostream>
+
+#include "plugin.hxx"
+#include "compat.hxx"
+#include "clang/AST/CXXInheritance.h"
+
+// Idea from bubli. Check that the index variable in a for loop is able to cover the range
+// revealed by the terminating condition.
+// If not, we might end up in an endless loop, or just not process certain parts.
+
+namespace {
+
+class LoopVarTooSmall:
+ public RecursiveASTVisitor<LoopVarTooSmall>, public loplugin::Plugin
+{
+public:
+ explicit LoopVarTooSmall(InstantiationData const & data): Plugin(data) {}
+
+ virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
+
+ bool VisitForStmt( const ForStmt* stmt );
+
+private:
+ StringRef getFilename(SourceLocation loc);
+
+};
+
+StringRef LoopVarTooSmall::getFilename(SourceLocation loc)
+{
+ SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(loc);
+ StringRef name { compiler.getSourceManager().getFilename(spellingLocation) };
+ return name;
+}
+
+bool LoopVarTooSmall::VisitForStmt( const ForStmt* stmt )
+{
+ if (ignoreLocation( stmt ))
+ return true;
+ // ignore SAL for now
+ StringRef aFileName = getFilename(stmt->getLocStart());
+ if (aFileName.startswith(SRCDIR "/sal/")) {
+ return true;
+ }
+
+ const Stmt* initStmt = stmt->getInit();
+ if (!initStmt || !isa<DeclStmt>(initStmt))
+ return true;
+ const DeclStmt* declStmt = dyn_cast<DeclStmt>(initStmt);
+ if (!declStmt->getDeclGroup().isSingleDecl())
+ return true;
+ const Decl* decl = declStmt->getSingleDecl();
+ if (!decl || !isa<VarDecl>(decl))
+ return true;
+ const VarDecl* varDecl = dyn_cast<VarDecl>(decl);
+ QualType qt = varDecl->getType();
+ if (!qt->isIntegralType(compiler.getASTContext()))
+ return true;
+ uint64_t qt1BitWidth = compiler.getASTContext().getTypeSize(qt);
+
+ if (!stmt->getCond() || !isa<BinaryOperator>(stmt->getCond()))
+ return true;
+ const BinaryOperator* binOp = dyn_cast<BinaryOperator>(stmt->getCond());
+ if (binOp->getOpcode() != BO_LT && binOp->getOpcode() != BO_LE)
+ return true;
+ const Expr* binOpRHS = binOp->getRHS();
+ // ignore complex expressions for now
+ if (isa<BinaryOperator>(binOpRHS))
+ return true;
+ if (isa<ImplicitCastExpr>(binOpRHS))
+ {
+ const ImplicitCastExpr* castExpr = dyn_cast<ImplicitCastExpr>(binOpRHS);
+ binOpRHS = castExpr->getSubExpr();
+ }
+ QualType qt2 = binOpRHS->getType();
+ if (!qt2->isIntegralType(compiler.getASTContext()))
+ return true;
+ // check for comparison with constants where the compiler just tends to give me the type as "int"
+ llvm::APSInt aIntResult;
+ uint64_t qt2BitWidth = compiler.getASTContext().getTypeSize(qt2);
+ if (binOpRHS->EvaluateAsInt(aIntResult, compiler.getASTContext())) {
+ if (aIntResult.getSExtValue() > 0 && aIntResult.getSExtValue() < 1<<7) {
+ qt2BitWidth = 8;
+ }
+ else if (aIntResult.getSExtValue() > 0 && aIntResult.getSExtValue() < 1<<15) {
+ qt2BitWidth = 16;
+ }
+ else if (aIntResult.getSExtValue() > 0 && aIntResult.getSExtValue() < 1L<<31) {
+ qt2BitWidth = 32;
+ }
+ }
+
+ if (qt1BitWidth < qt2BitWidth)
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "loop index variable is shorter than length type. " + qt.getAsString() + " < " + qt2.getAsString(),
+ stmt->getLocStart())
+ << stmt->getSourceRange();
+// stmt->getCond()->dump();
+ }
+ return true;
+}
+
+
+loplugin::Plugin::Registration< LoopVarTooSmall > X("loopvartoosmall", false);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */