1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
16 //#include "clang/AST/CXXInheritance.h"
18 // Idea from bubli. Check that the index variable in a for loop is able to cover the range
19 // revealed by the terminating condition.
20 // If not, we might end up in an endless loop, or just not process certain parts.
25 class LoopVarTooSmall
:
26 public RecursiveASTVisitor
<LoopVarTooSmall
>, public loplugin::Plugin
29 explicit LoopVarTooSmall(loplugin::InstantiationData
const & data
):
32 virtual void run() override
{
33 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
36 bool VisitForStmt( const ForStmt
* stmt
) {
37 checkExpr(stmt
->getCond());
41 bool VisitWhileStmt(WhileStmt
const * stmt
) {
42 checkExpr(stmt
->getCond());
46 bool VisitDoStmt(DoStmt
const * stmt
) {
47 checkExpr(stmt
->getCond());
52 unsigned getIntValueWidth(QualType type
) const;
54 void checkSubExpr(Expr
const * expr
, bool positive
);
56 void checkExpr(Expr
const * expr
);
59 BinaryOperator
const * op
;
64 std::list
<Comparison
> comparisons
;
68 std::map
<Decl
const *, Comparisons
> comparisons_
;
71 unsigned LoopVarTooSmall::getIntValueWidth(QualType type
) const {
72 if (auto const et
= type
->getAs
<EnumType
>()) {
73 auto const ed
= et
->getDecl();
75 unsigned pos
= ed
->getNumPositiveBits();
76 unsigned neg
= ed
->getNumNegativeBits();
77 return neg
== 0 ? std::max(pos
, 1U) : std::max(pos
+ 1, neg
);
80 return compiler
.getASTContext().getIntWidth(type
);
83 void LoopVarTooSmall::checkSubExpr(Expr
const * expr
, bool positive
) {
84 auto const e
= expr
->IgnoreImplicit()->IgnoreParenImpCasts();
85 if (auto const uo
= dyn_cast
<UnaryOperator
>(e
)) {
86 if (uo
->getOpcode() == UO_LNot
) {
87 checkSubExpr(uo
->getSubExpr(), !positive
);
91 const BinaryOperator
* binOp
= dyn_cast
<BinaryOperator
>(e
);
96 switch (binOp
->getOpcode()) {
98 checkSubExpr(binOp
->getLHS(), true);
99 checkSubExpr(binOp
->getRHS(), true);
112 switch (binOp
->getOpcode()) {
114 checkSubExpr(binOp
->getLHS(), false);
115 checkSubExpr(binOp
->getRHS(), false);
128 auto lhs
= dyn_cast
<DeclRefExpr
>(binOp
->getLHS()->IgnoreParenImpCasts());
131 QualType qt
= lhs
->getType();
132 if (!qt
->isIntegralOrEnumerationType())
134 unsigned qt1BitWidth
= getIntValueWidth(qt
);
135 auto lhsDecl
= lhs
->getDecl();
136 if (!isa
<VarDecl
>(lhsDecl
)) {
137 if (auto fd
= dyn_cast
<FieldDecl
>(lhsDecl
)) {
138 if (fd
->isBitField()) {
139 qt1BitWidth
= std::max(
141 fd
->getBitWidthValue(compiler
.getASTContext()));
147 const Expr
* binOpRHS
= binOp
->getRHS()->IgnoreParenImpCasts();
148 QualType qt2
= binOpRHS
->getType();
149 if (!qt2
->isIntegralType(compiler
.getASTContext()))
151 unsigned qt2BitWidth
;
152 llvm::APSInt aIntResult
;
153 // Work around missing Clang 3.9 fix <https://reviews.llvm.org/rL271762>
154 // "Sema: do not attempt to sizeof a dependent type", causing Clang 3.8 to
155 // crash during EvaluateAsInt() on expressions of the form
159 // with dependent type T:
160 if (!binOpRHS
->isValueDependent()
161 && binOpRHS
->EvaluateAsInt(aIntResult
, compiler
.getASTContext()))
163 if (less
&& aIntResult
.isStrictlyPositive()) {
166 qt2BitWidth
= aIntResult
.isUnsigned() || !aIntResult
.isNegative()
167 ? std::max(aIntResult
.getActiveBits(), 1U)
168 : aIntResult
.getBitWidth() - aIntResult
.countLeadingOnes() + 1;
170 // Ignore complex expressions for now, promotion rules on conditions
171 // like "i < (size()+1)" make it hard to guess at a correct type:
172 if (isa
<BinaryOperator
>(binOpRHS
) || isa
<ConditionalOperator
>(binOpRHS
))
176 qt2BitWidth
= getIntValueWidth(qt2
);
177 if (auto dre
= dyn_cast
<DeclRefExpr
>(binOpRHS
)) {
178 if (auto fd
= dyn_cast
<FieldDecl
>(dre
->getDecl())) {
179 if (fd
->isBitField()) {
180 qt2BitWidth
= std::max(
182 fd
->getBitWidthValue(compiler
.getASTContext()));
188 auto i
= comparisons_
.find(lhsDecl
);
189 if (i
== comparisons_
.end()) {
190 i
= (comparisons_
.insert(
191 decltype(comparisons_
)::value_type(lhsDecl
, {{}, qt1BitWidth
}))
194 assert(i
->second
.lhsWidth
== qt1BitWidth
);
197 for (auto j
= i
->second
.comparisons
.begin();
198 j
!= i
->second
.comparisons
.end();)
200 if (qt2BitWidth
> j
->rhsWidth
) {
203 } else if (qt2BitWidth
< j
->rhsWidth
) {
204 j
= i
->second
.comparisons
.erase(j
);
210 i
->second
.comparisons
.push_back({binOp
, qt2BitWidth
});
214 void LoopVarTooSmall::checkExpr(Expr
const * expr
) {
215 if (expr
!= nullptr && !ignoreLocation(expr
)) {
216 assert(comparisons_
.empty());
217 checkSubExpr(expr
, true);
218 for (auto const & i
: comparisons_
) {
219 for (auto const & j
: i
.second
.comparisons
) {
220 if (i
.second
.lhsWidth
< j
.rhsWidth
) {
222 DiagnosticsEngine::Warning
,
223 "loop index type %0 is narrower than length type %1",
225 << j
.op
->getLHS()->IgnoreImpCasts()->getType()
226 << j
.op
->getRHS()->IgnoreImpCasts()->getType()
227 << j
.op
->getSourceRange();
231 comparisons_
.clear();
235 loplugin::Plugin::Registration
< LoopVarTooSmall
> X("loopvartoosmall");
239 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */