diff options
author | Noel Grandin <noel@peralex.com> | 2015-05-27 16:33:44 +0200 |
---|---|---|
committer | Noel Grandin <noel@peralex.com> | 2015-05-28 12:49:54 +0200 |
commit | d5129a9dd68978f9eccdd4597b5b6834557c422a (patch) | |
tree | df43250172f784f3048ce42ce1c3410d1d449c1e /compilerplugins/clang/loopvartoosmall.cxx | |
parent | f3331f7694e74f349375c223ce7ed84838e92d89 (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/clang/loopvartoosmall.cxx')
-rw-r--r-- | compilerplugins/clang/loopvartoosmall.cxx | 119 |
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: */ |