1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #ifndef LO_CLANG_SHARED_PLUGINS
21 //#include "clang/AST/CXXInheritance.h"
23 // Idea from bubli. Check that the index variable in a for loop is able to cover the range
24 // revealed by the terminating condition.
25 // If not, we might end up in an endless loop, or just not process certain parts.
30 class LoopVarTooSmall
:
31 public loplugin::FilteringPlugin
<LoopVarTooSmall
>
34 explicit LoopVarTooSmall(loplugin::InstantiationData
const & data
):
35 FilteringPlugin(data
) {}
37 virtual void run() override
{
38 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
41 bool VisitForStmt( const ForStmt
* stmt
) {
42 checkExpr(stmt
->getCond());
46 bool VisitWhileStmt(WhileStmt
const * stmt
) {
47 checkExpr(stmt
->getCond());
51 bool VisitDoStmt(DoStmt
const * stmt
) {
52 checkExpr(stmt
->getCond());
57 unsigned getIntValueWidth(QualType type
) const;
59 void checkSubExpr(Expr
const * expr
, bool positive
);
61 void checkExpr(Expr
const * expr
);
64 BinaryOperator
const * op
;
69 std::list
<Comparison
> comparisons
;
73 std::map
<Decl
const *, Comparisons
> comparisons_
;
76 unsigned LoopVarTooSmall::getIntValueWidth(QualType type
) const {
77 if (auto const et
= type
->getAs
<EnumType
>()) {
78 auto const ed
= et
->getDecl();
80 unsigned pos
= ed
->getNumPositiveBits();
81 unsigned neg
= ed
->getNumNegativeBits();
82 return neg
== 0 ? std::max(pos
, 1U) : std::max(pos
+ 1, neg
);
85 return compiler
.getASTContext().getIntWidth(type
);
88 void LoopVarTooSmall::checkSubExpr(Expr
const * expr
, bool positive
) {
89 auto const e
= expr
->IgnoreImplicit()->IgnoreParenImpCasts();
90 if (auto const uo
= dyn_cast
<UnaryOperator
>(e
)) {
91 if (uo
->getOpcode() == UO_LNot
) {
92 checkSubExpr(uo
->getSubExpr(), !positive
);
96 const BinaryOperator
* binOp
= dyn_cast
<BinaryOperator
>(e
);
101 switch (binOp
->getOpcode()) {
103 checkSubExpr(binOp
->getLHS(), true);
104 checkSubExpr(binOp
->getRHS(), true);
117 switch (binOp
->getOpcode()) {
119 checkSubExpr(binOp
->getLHS(), false);
120 checkSubExpr(binOp
->getRHS(), false);
133 auto lhs
= dyn_cast
<DeclRefExpr
>(binOp
->getLHS()->IgnoreParenImpCasts());
136 QualType qt
= lhs
->getType();
137 if (!qt
->isIntegralOrEnumerationType())
139 unsigned qt1BitWidth
= getIntValueWidth(qt
);
140 auto lhsDecl
= lhs
->getDecl();
141 if (!isa
<VarDecl
>(lhsDecl
)) {
142 if (auto fd
= dyn_cast
<FieldDecl
>(lhsDecl
)) {
143 if (fd
->isBitField()) {
144 qt1BitWidth
= std::max(
146 fd
->getBitWidthValue(compiler
.getASTContext()));
152 const Expr
* binOpRHS
= binOp
->getRHS()->IgnoreParenImpCasts();
153 QualType qt2
= binOpRHS
->getType();
154 if (!qt2
->isIntegralType(compiler
.getASTContext()))
156 unsigned qt2BitWidth
;
157 llvm::APSInt aIntResult
;
158 // Work around missing Clang 3.9 fix <https://reviews.llvm.org/rL271762>
159 // "Sema: do not attempt to sizeof a dependent type", causing Clang 3.8 to
160 // crash during EvaluateAsInt() on expressions of the form
164 // with dependent type T:
165 if (!binOpRHS
->isValueDependent()
166 && compat::EvaluateAsInt(binOpRHS
, aIntResult
, compiler
.getASTContext()))
168 if (less
&& aIntResult
.isStrictlyPositive()) {
171 qt2BitWidth
= aIntResult
.isUnsigned() || !aIntResult
.isNegative()
172 ? std::max(aIntResult
.getActiveBits(), 1U)
173 : aIntResult
.getBitWidth() - aIntResult
.countLeadingOnes() + 1;
175 // Ignore complex expressions for now, promotion rules on conditions
176 // like "i < (size()+1)" make it hard to guess at a correct type:
177 if (isa
<BinaryOperator
>(binOpRHS
) || isa
<ConditionalOperator
>(binOpRHS
))
181 qt2BitWidth
= getIntValueWidth(qt2
);
182 if (auto dre
= dyn_cast
<DeclRefExpr
>(binOpRHS
)) {
183 if (auto fd
= dyn_cast
<FieldDecl
>(dre
->getDecl())) {
184 if (fd
->isBitField()) {
185 qt2BitWidth
= std::max(
187 fd
->getBitWidthValue(compiler
.getASTContext()));
193 auto i
= comparisons_
.find(lhsDecl
);
194 if (i
== comparisons_
.end()) {
195 i
= (comparisons_
.insert(
196 decltype(comparisons_
)::value_type(lhsDecl
, {{}, qt1BitWidth
}))
199 assert(i
->second
.lhsWidth
== qt1BitWidth
);
202 for (auto j
= i
->second
.comparisons
.begin();
203 j
!= i
->second
.comparisons
.end();)
205 if (qt2BitWidth
> j
->rhsWidth
) {
208 } else if (qt2BitWidth
< j
->rhsWidth
) {
209 j
= i
->second
.comparisons
.erase(j
);
215 i
->second
.comparisons
.push_back({binOp
, qt2BitWidth
});
219 void LoopVarTooSmall::checkExpr(Expr
const * expr
) {
220 if (expr
!= nullptr && !ignoreLocation(expr
)) {
221 assert(comparisons_
.empty());
222 checkSubExpr(expr
, true);
223 for (auto const & i
: comparisons_
) {
224 for (auto const & j
: i
.second
.comparisons
) {
225 if (i
.second
.lhsWidth
< j
.rhsWidth
) {
227 DiagnosticsEngine::Warning
,
228 "loop index type %0 is narrower than length type %1",
230 << j
.op
->getLHS()->IgnoreImpCasts()->getType()
231 << j
.op
->getRHS()->IgnoreImpCasts()->getType()
232 << j
.op
->getSourceRange();
236 comparisons_
.clear();
240 loplugin::Plugin::Registration
< LoopVarTooSmall
> loopvartoosmall("loopvartoosmall");
244 #endif // LO_CLANG_SHARED_PLUGINS
246 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */