1 //===--- TooSmallLoopVariableCheck.cpp - clang-tidy -----------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "TooSmallLoopVariableCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
17 static constexpr llvm::StringLiteral LoopName
=
18 llvm::StringLiteral("forLoopName");
19 static constexpr llvm::StringLiteral LoopVarName
=
20 llvm::StringLiteral("loopVar");
21 static constexpr llvm::StringLiteral LoopVarCastName
=
22 llvm::StringLiteral("loopVarCast");
23 static constexpr llvm::StringLiteral LoopUpperBoundName
=
24 llvm::StringLiteral("loopUpperBound");
25 static constexpr llvm::StringLiteral LoopIncrementName
=
26 llvm::StringLiteral("loopIncrement");
30 struct MagnitudeBits
{
31 unsigned WidthWithoutSignBit
= 0U;
32 unsigned BitFieldWidth
= 0U;
34 bool operator<(const MagnitudeBits
&Other
) const noexcept
{
35 return WidthWithoutSignBit
< Other
.WidthWithoutSignBit
;
38 bool operator!=(const MagnitudeBits
&Other
) const noexcept
{
39 return WidthWithoutSignBit
!= Other
.WidthWithoutSignBit
||
40 BitFieldWidth
!= Other
.BitFieldWidth
;
46 TooSmallLoopVariableCheck::TooSmallLoopVariableCheck(StringRef Name
,
47 ClangTidyContext
*Context
)
48 : ClangTidyCheck(Name
, Context
),
49 MagnitudeBitsUpperLimit(Options
.get("MagnitudeBitsUpperLimit", 16U)) {}
51 void TooSmallLoopVariableCheck::storeOptions(
52 ClangTidyOptions::OptionMap
&Opts
) {
53 Options
.store(Opts
, "MagnitudeBitsUpperLimit", MagnitudeBitsUpperLimit
);
56 /// The matcher for loops with suspicious integer loop variable.
58 /// In this general example, assuming 'j' and 'k' are of integral type:
60 /// for (...; j < 3 + 2; ++k) { ... }
62 /// The following string identifiers are bound to these parts of the AST:
63 /// LoopVarName: 'j' (as a VarDecl)
64 /// LoopVarCastName: 'j' (after implicit conversion)
65 /// LoopUpperBoundName: '3 + 2' (as an Expr)
66 /// LoopIncrementName: 'k' (as an Expr)
67 /// LoopName: The entire for loop (as a ForStmt)
69 void TooSmallLoopVariableCheck::registerMatchers(MatchFinder
*Finder
) {
70 StatementMatcher LoopVarMatcher
=
71 expr(ignoringParenImpCasts(
72 anyOf(declRefExpr(to(varDecl(hasType(isInteger())))),
73 memberExpr(member(fieldDecl(hasType(isInteger())))))))
76 // We need to catch only those comparisons which contain any integer cast.
77 StatementMatcher LoopVarConversionMatcher
= traverse(
78 TK_AsIs
, implicitCastExpr(hasImplicitDestinationType(isInteger()),
79 has(ignoringParenImpCasts(LoopVarMatcher
)))
80 .bind(LoopVarCastName
));
82 // We are interested in only those cases when the loop bound is a variable
83 // value (not const, enum, etc.).
84 StatementMatcher LoopBoundMatcher
=
85 expr(ignoringParenImpCasts(allOf(hasType(isInteger()),
86 unless(integerLiteral()),
87 unless(hasType(isConstQualified())),
88 unless(hasType(enumType())))))
89 .bind(LoopUpperBoundName
);
91 // We use the loop increment expression only to make sure we found the right
93 StatementMatcher IncrementMatcher
=
94 expr(ignoringParenImpCasts(hasType(isInteger()))).bind(LoopIncrementName
);
99 binaryOperator(hasOperatorName("<"),
100 hasLHS(LoopVarConversionMatcher
),
101 hasRHS(LoopBoundMatcher
)),
102 binaryOperator(hasOperatorName("<="),
103 hasLHS(LoopVarConversionMatcher
),
104 hasRHS(LoopBoundMatcher
)),
105 binaryOperator(hasOperatorName(">"), hasLHS(LoopBoundMatcher
),
106 hasRHS(LoopVarConversionMatcher
)),
107 binaryOperator(hasOperatorName(">="), hasLHS(LoopBoundMatcher
),
108 hasRHS(LoopVarConversionMatcher
)))),
109 hasIncrement(IncrementMatcher
))
114 /// Returns the magnitude bits of an integer type.
115 static MagnitudeBits
calcMagnitudeBits(const ASTContext
&Context
,
116 const QualType
&IntExprType
,
117 const Expr
*IntExpr
) {
118 assert(IntExprType
->isIntegerType());
120 unsigned SignedBits
= IntExprType
->isUnsignedIntegerType() ? 0U : 1U;
122 if (const auto *BitField
= IntExpr
->getSourceBitField()) {
123 unsigned BitFieldWidth
= BitField
->getBitWidthValue(Context
);
124 return {BitFieldWidth
- SignedBits
, BitFieldWidth
};
127 unsigned IntWidth
= Context
.getIntWidth(IntExprType
);
128 return {IntWidth
- SignedBits
, 0U};
131 /// Calculate the upper bound expression's magnitude bits, but ignore
132 /// constant like values to reduce false positives.
134 calcUpperBoundMagnitudeBits(const ASTContext
&Context
, const Expr
*UpperBound
,
135 const QualType
&UpperBoundType
) {
136 // Ignore casting caused by constant values inside a binary operator.
137 // We are interested in variable values' magnitude bits.
138 if (const auto *BinOperator
= dyn_cast
<BinaryOperator
>(UpperBound
)) {
139 const Expr
*RHSE
= BinOperator
->getRHS()->IgnoreParenImpCasts();
140 const Expr
*LHSE
= BinOperator
->getLHS()->IgnoreParenImpCasts();
142 QualType RHSEType
= RHSE
->getType();
143 QualType LHSEType
= LHSE
->getType();
145 if (!RHSEType
->isIntegerType() || !LHSEType
->isIntegerType())
148 bool RHSEIsConstantValue
= RHSEType
->isEnumeralType() ||
149 RHSEType
.isConstQualified() ||
150 isa
<IntegerLiteral
>(RHSE
);
151 bool LHSEIsConstantValue
= LHSEType
->isEnumeralType() ||
152 LHSEType
.isConstQualified() ||
153 isa
<IntegerLiteral
>(LHSE
);
155 // Avoid false positives produced by two constant values.
156 if (RHSEIsConstantValue
&& LHSEIsConstantValue
)
158 if (RHSEIsConstantValue
)
159 return calcMagnitudeBits(Context
, LHSEType
, LHSE
);
160 if (LHSEIsConstantValue
)
161 return calcMagnitudeBits(Context
, RHSEType
, RHSE
);
163 return std::max(calcMagnitudeBits(Context
, LHSEType
, LHSE
),
164 calcMagnitudeBits(Context
, RHSEType
, RHSE
));
167 return calcMagnitudeBits(Context
, UpperBoundType
, UpperBound
);
170 static std::string
formatIntegralType(const QualType
&Type
,
171 const MagnitudeBits
&Info
) {
172 std::string Name
= Type
.getAsString();
173 if (!Info
.BitFieldWidth
)
177 Name
+= std::to_string(Info
.BitFieldWidth
);
181 void TooSmallLoopVariableCheck::check(const MatchFinder::MatchResult
&Result
) {
182 const auto *LoopVar
= Result
.Nodes
.getNodeAs
<Expr
>(LoopVarName
);
183 const auto *UpperBound
=
184 Result
.Nodes
.getNodeAs
<Expr
>(LoopUpperBoundName
)->IgnoreParenImpCasts();
185 const auto *LoopIncrement
=
186 Result
.Nodes
.getNodeAs
<Expr
>(LoopIncrementName
)->IgnoreParenImpCasts();
188 // We matched the loop variable incorrectly.
189 if (LoopVar
->getType() != LoopIncrement
->getType())
192 ASTContext
&Context
= *Result
.Context
;
194 const QualType LoopVarType
= LoopVar
->getType();
195 const MagnitudeBits LoopVarMagnitudeBits
=
196 calcMagnitudeBits(Context
, LoopVarType
, LoopVar
);
198 const MagnitudeBits LoopIncrementMagnitudeBits
=
199 calcMagnitudeBits(Context
, LoopIncrement
->getType(), LoopIncrement
);
200 // We matched the loop variable incorrectly.
201 if (LoopIncrementMagnitudeBits
!= LoopVarMagnitudeBits
)
204 const QualType UpperBoundType
= UpperBound
->getType();
205 const MagnitudeBits UpperBoundMagnitudeBits
=
206 calcUpperBoundMagnitudeBits(Context
, UpperBound
, UpperBoundType
);
208 if ((0U == UpperBoundMagnitudeBits
.WidthWithoutSignBit
) ||
209 (LoopVarMagnitudeBits
.WidthWithoutSignBit
> MagnitudeBitsUpperLimit
) ||
210 (LoopVarMagnitudeBits
.WidthWithoutSignBit
>=
211 UpperBoundMagnitudeBits
.WidthWithoutSignBit
))
214 diag(LoopVar
->getBeginLoc(),
215 "loop variable has narrower type '%0' than iteration's upper bound '%1'")
216 << formatIntegralType(LoopVarType
, LoopVarMagnitudeBits
)
217 << formatIntegralType(UpperBoundType
, UpperBoundMagnitudeBits
);
220 } // namespace clang::tidy::bugprone