1 //===--- MagicNumbersCheck.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 // A checker for magic numbers: integer or floating point literals embedded
10 // in the code, outside the definition of a constant or an enumeration.
12 //===----------------------------------------------------------------------===//
14 #include "MagicNumbersCheck.h"
15 #include "../utils/OptionsUtils.h"
16 #include "clang/AST/ASTContext.h"
17 #include "clang/AST/ASTTypeTraits.h"
18 #include "clang/AST/Type.h"
19 #include "clang/ASTMatchers/ASTMatchFinder.h"
20 #include "llvm/ADT/STLExtras.h"
23 using namespace clang::ast_matchers
;
27 static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult
&Result
,
28 const DynTypedNode
&Node
) {
30 const auto *AsDecl
= Node
.get
<DeclaratorDecl
>();
32 if (AsDecl
->getType().isConstQualified())
35 return AsDecl
->isImplicit();
38 if (Node
.get
<EnumConstantDecl
>())
41 return llvm::any_of(Result
.Context
->getParents(Node
),
42 [&Result
](const DynTypedNode
&Parent
) {
43 return isUsedToInitializeAConstant(Result
, Parent
);
47 static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult
&Result
,
48 const DynTypedNode
&Node
) {
50 if (Node
.get
<TypeAliasDecl
>() || Node
.get
<TypedefNameDecl
>())
53 return llvm::any_of(Result
.Context
->getParents(Node
),
54 [&Result
](const DynTypedNode
&Parent
) {
55 return isUsedToDefineATypeAlias(Result
, Parent
);
59 static bool isUsedToDefineABitField(const MatchFinder::MatchResult
&Result
,
60 const DynTypedNode
&Node
) {
61 const auto *AsFieldDecl
= Node
.get
<FieldDecl
>();
62 if (AsFieldDecl
&& AsFieldDecl
->isBitField())
65 return llvm::any_of(Result
.Context
->getParents(Node
),
66 [&Result
](const DynTypedNode
&Parent
) {
67 return isUsedToDefineABitField(Result
, Parent
);
71 namespace tidy::readability
{
73 const char DefaultIgnoredIntegerValues
[] = "1;2;3;4;";
74 const char DefaultIgnoredFloatingPointValues
[] = "1.0;100.0;";
76 MagicNumbersCheck::MagicNumbersCheck(StringRef Name
, ClangTidyContext
*Context
)
77 : ClangTidyCheck(Name
, Context
),
78 IgnoreAllFloatingPointValues(
79 Options
.get("IgnoreAllFloatingPointValues", false)),
80 IgnoreBitFieldsWidths(Options
.get("IgnoreBitFieldsWidths", true)),
81 IgnorePowersOf2IntegerValues(
82 Options
.get("IgnorePowersOf2IntegerValues", false)),
83 IgnoreTypeAliases(Options
.get("IgnoreTypeAliases", false)),
84 IgnoreUserDefinedLiterals(
85 Options
.get("IgnoreUserDefinedLiterals", false)),
86 RawIgnoredIntegerValues(
87 Options
.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues
)),
88 RawIgnoredFloatingPointValues(Options
.get(
89 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues
)) {
90 // Process the set of ignored integer values.
91 const std::vector
<StringRef
> IgnoredIntegerValuesInput
=
92 utils::options::parseStringList(RawIgnoredIntegerValues
);
93 IgnoredIntegerValues
.resize(IgnoredIntegerValuesInput
.size());
94 llvm::transform(IgnoredIntegerValuesInput
, IgnoredIntegerValues
.begin(),
97 Value
.getAsInteger(10, Res
);
100 llvm::sort(IgnoredIntegerValues
);
102 if (!IgnoreAllFloatingPointValues
) {
103 // Process the set of ignored floating point values.
104 const std::vector
<StringRef
> IgnoredFloatingPointValuesInput
=
105 utils::options::parseStringList(RawIgnoredFloatingPointValues
);
106 IgnoredFloatingPointValues
.reserve(IgnoredFloatingPointValuesInput
.size());
107 IgnoredDoublePointValues
.reserve(IgnoredFloatingPointValuesInput
.size());
108 for (const auto &InputValue
: IgnoredFloatingPointValuesInput
) {
109 llvm::APFloat
FloatValue(llvm::APFloat::IEEEsingle());
111 FloatValue
.convertFromString(InputValue
, DefaultRoundingMode
);
112 assert(StatusOrErr
&& "Invalid floating point representation");
113 consumeError(StatusOrErr
.takeError());
114 IgnoredFloatingPointValues
.push_back(FloatValue
.convertToFloat());
116 llvm::APFloat
DoubleValue(llvm::APFloat::IEEEdouble());
118 DoubleValue
.convertFromString(InputValue
, DefaultRoundingMode
);
119 assert(StatusOrErr
&& "Invalid floating point representation");
120 consumeError(StatusOrErr
.takeError());
121 IgnoredDoublePointValues
.push_back(DoubleValue
.convertToDouble());
123 llvm::sort(IgnoredFloatingPointValues
);
124 llvm::sort(IgnoredDoublePointValues
);
128 void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
129 Options
.store(Opts
, "IgnoreAllFloatingPointValues",
130 IgnoreAllFloatingPointValues
);
131 Options
.store(Opts
, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths
);
132 Options
.store(Opts
, "IgnorePowersOf2IntegerValues",
133 IgnorePowersOf2IntegerValues
);
134 Options
.store(Opts
, "IgnoreTypeAliases", IgnoreTypeAliases
);
135 Options
.store(Opts
, "IgnoreUserDefinedLiterals", IgnoreUserDefinedLiterals
);
136 Options
.store(Opts
, "IgnoredIntegerValues", RawIgnoredIntegerValues
);
137 Options
.store(Opts
, "IgnoredFloatingPointValues",
138 RawIgnoredFloatingPointValues
);
141 void MagicNumbersCheck::registerMatchers(MatchFinder
*Finder
) {
142 Finder
->addMatcher(integerLiteral().bind("integer"), this);
143 if (!IgnoreAllFloatingPointValues
)
144 Finder
->addMatcher(floatLiteral().bind("float"), this);
147 void MagicNumbersCheck::check(const MatchFinder::MatchResult
&Result
) {
149 TraversalKindScope
RAII(*Result
.Context
, TK_AsIs
);
151 checkBoundMatch
<IntegerLiteral
>(Result
, "integer");
152 checkBoundMatch
<FloatingLiteral
>(Result
, "float");
155 bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult
&Result
,
156 const Expr
&ExprResult
) const {
158 Result
.Context
->getParents(ExprResult
),
159 [this, &Result
](const DynTypedNode
&Parent
) {
160 if (isUsedToInitializeAConstant(Result
, Parent
))
163 if (IgnoreTypeAliases
&& isUsedToDefineATypeAlias(Result
, Parent
))
166 // Ignore this instance, because this matches an
167 // expanded class enumeration value.
168 if (Parent
.get
<CStyleCastExpr
>() &&
170 Result
.Context
->getParents(Parent
),
171 [](const DynTypedNode
&GrandParent
) {
172 return GrandParent
.get
<SubstNonTypeTemplateParmExpr
>() !=
177 // Ignore this instance, because this match reports the
178 // location where the template is defined, not where it
180 if (Parent
.get
<SubstNonTypeTemplateParmExpr
>())
183 // Don't warn on string user defined literals:
184 // std::string s = "Hello World"s;
185 if (const auto *UDL
= Parent
.get
<UserDefinedLiteral
>())
186 if (UDL
->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String
)
193 bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral
*Literal
) const {
194 if (Literal
->getType()->isBitIntType()) {
197 const llvm::APInt IntValue
= Literal
->getValue();
198 const int64_t Value
= IntValue
.getZExtValue();
202 if (IgnorePowersOf2IntegerValues
&& IntValue
.isPowerOf2())
205 return std::binary_search(IgnoredIntegerValues
.begin(),
206 IgnoredIntegerValues
.end(), Value
);
209 bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral
*Literal
) const {
210 const llvm::APFloat FloatValue
= Literal
->getValue();
211 if (FloatValue
.isZero())
214 if (&FloatValue
.getSemantics() == &llvm::APFloat::IEEEsingle()) {
215 const float Value
= FloatValue
.convertToFloat();
216 return std::binary_search(IgnoredFloatingPointValues
.begin(),
217 IgnoredFloatingPointValues
.end(), Value
);
220 if (&FloatValue
.getSemantics() == &llvm::APFloat::IEEEdouble()) {
221 const double Value
= FloatValue
.convertToDouble();
222 return std::binary_search(IgnoredDoublePointValues
.begin(),
223 IgnoredDoublePointValues
.end(), Value
);
229 bool MagicNumbersCheck::isSyntheticValue(const SourceManager
*SourceManager
,
230 const IntegerLiteral
*Literal
) const {
231 const std::pair
<FileID
, unsigned> FileOffset
=
232 SourceManager
->getDecomposedLoc(Literal
->getLocation());
233 if (FileOffset
.first
.isInvalid())
236 const StringRef BufferIdentifier
=
237 SourceManager
->getBufferOrFake(FileOffset
.first
).getBufferIdentifier();
239 return BufferIdentifier
.empty();
242 bool MagicNumbersCheck::isBitFieldWidth(
243 const clang::ast_matchers::MatchFinder::MatchResult
&Result
,
244 const IntegerLiteral
&Literal
) const {
245 return IgnoreBitFieldsWidths
&&
246 llvm::any_of(Result
.Context
->getParents(Literal
),
247 [&Result
](const DynTypedNode
&Parent
) {
248 return isUsedToDefineABitField(Result
, Parent
);
252 bool MagicNumbersCheck::isUserDefinedLiteral(
253 const clang::ast_matchers::MatchFinder::MatchResult
&Result
,
254 const clang::Expr
&Literal
) const {
255 DynTypedNodeList Parents
= Result
.Context
->getParents(Literal
);
258 return Parents
[0].get
<UserDefinedLiteral
>() != nullptr;
261 } // namespace tidy::readability