clang-tools 22.0.0git
MagicNumbersCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
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.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MagicNumbersCheck.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"
21
22using namespace clang::ast_matchers;
23
24namespace clang {
25
26static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
27 const DynTypedNode &Node) {
28 const auto *AsDecl = Node.get<DeclaratorDecl>();
29 if (AsDecl) {
30 if (AsDecl->getType().isConstQualified())
31 return true;
32
33 return AsDecl->isImplicit();
34 }
35
36 if (Node.get<EnumConstantDecl>())
37 return true;
38
39 return llvm::any_of(Result.Context->getParents(Node),
40 [&Result](const DynTypedNode &Parent) {
41 return isUsedToInitializeAConstant(Result, Parent);
42 });
43}
44
45static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result,
46 const DynTypedNode &Node) {
47 if (Node.get<TypeAliasDecl>() || Node.get<TypedefNameDecl>())
48 return true;
49
50 return llvm::any_of(Result.Context->getParents(Node),
51 [&Result](const DynTypedNode &Parent) {
52 return isUsedToDefineATypeAlias(Result, Parent);
53 });
54}
55
56static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,
57 const DynTypedNode &Node) {
58 const auto *AsFieldDecl = Node.get<FieldDecl>();
59 if (AsFieldDecl && AsFieldDecl->isBitField())
60 return true;
61
62 return llvm::any_of(Result.Context->getParents(Node),
63 [&Result](const DynTypedNode &Parent) {
64 return isUsedToDefineABitField(Result, Parent);
65 });
66}
67
68namespace tidy::readability {
69
70const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";
71const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";
72
74 : ClangTidyCheck(Name, Context),
75 IgnoreAllFloatingPointValues(
76 Options.get("IgnoreAllFloatingPointValues", false)),
77 IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),
78 IgnorePowersOf2IntegerValues(
79 Options.get("IgnorePowersOf2IntegerValues", false)),
80 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)),
81 IgnoreUserDefinedLiterals(
82 Options.get("IgnoreUserDefinedLiterals", false)),
83 RawIgnoredIntegerValues(
84 Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),
85 RawIgnoredFloatingPointValues(Options.get(
86 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {
87 // Process the set of ignored integer values.
88 const std::vector<StringRef> IgnoredIntegerValuesInput =
89 utils::options::parseStringList(RawIgnoredIntegerValues);
90 IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
91 llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
92 [](StringRef Value) {
93 int64_t Res = 0;
94 Value.getAsInteger(10, Res);
95 return Res;
96 });
97 llvm::sort(IgnoredIntegerValues);
98
99 if (!IgnoreAllFloatingPointValues) {
100 // Process the set of ignored floating point values.
101 const std::vector<StringRef> IgnoredFloatingPointValuesInput =
102 utils::options::parseStringList(RawIgnoredFloatingPointValues);
103 IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
104 IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
105 for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
106 llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
107 auto StatusOrErr =
108 FloatValue.convertFromString(InputValue, DefaultRoundingMode);
109 assert(StatusOrErr && "Invalid floating point representation");
110 consumeError(StatusOrErr.takeError());
111 IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
112
113 llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
114 StatusOrErr =
115 DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
116 assert(StatusOrErr && "Invalid floating point representation");
117 consumeError(StatusOrErr.takeError());
118 IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
119 }
120 llvm::sort(IgnoredFloatingPointValues);
121 llvm::sort(IgnoredDoublePointValues);
122 }
123}
124
126 Options.store(Opts, "IgnoreAllFloatingPointValues",
127 IgnoreAllFloatingPointValues);
128 Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
129 Options.store(Opts, "IgnorePowersOf2IntegerValues",
130 IgnorePowersOf2IntegerValues);
131 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
132 Options.store(Opts, "IgnoreUserDefinedLiterals", IgnoreUserDefinedLiterals);
133 Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
134 Options.store(Opts, "IgnoredFloatingPointValues",
135 RawIgnoredFloatingPointValues);
136}
137
138void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
139 Finder->addMatcher(integerLiteral().bind("integer"), this);
140 if (!IgnoreAllFloatingPointValues)
141 Finder->addMatcher(floatLiteral().bind("float"), this);
142}
143
144void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
145 const TraversalKindScope RAII(*Result.Context, TK_AsIs);
146
147 checkBoundMatch<IntegerLiteral>(Result, "integer");
148 checkBoundMatch<FloatingLiteral>(Result, "float");
149}
150
151bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
152 const Expr &ExprResult) const {
153 return llvm::any_of(
154 Result.Context->getParents(ExprResult),
155 [this, &Result](const DynTypedNode &Parent) {
156 if (isUsedToInitializeAConstant(Result, Parent))
157 return true;
158
159 if (IgnoreTypeAliases && isUsedToDefineATypeAlias(Result, Parent))
160 return true;
161
162 // Ignore this instance, because this matches an
163 // expanded class enumeration value.
164 if (Parent.get<CStyleCastExpr>() &&
165 llvm::any_of(
166 Result.Context->getParents(Parent),
167 [](const DynTypedNode &GrandParent) {
168 return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
169 nullptr;
170 }))
171 return true;
172
173 // Ignore this instance, because this match reports the
174 // location where the template is defined, not where it
175 // is instantiated.
176 if (Parent.get<SubstNonTypeTemplateParmExpr>())
177 return true;
178
179 // Don't warn on string user defined literals:
180 // std::string s = "Hello World"s;
181 if (const auto *UDL = Parent.get<UserDefinedLiteral>())
182 if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
183 return true;
184
185 return false;
186 });
187}
188
189bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
190 if (Literal->getType()->isBitIntType()) {
191 return true;
192 }
193 const llvm::APInt IntValue = Literal->getValue();
194 const int64_t Value = IntValue.getZExtValue();
195 if (Value == 0)
196 return true;
197
198 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
199 return true;
200
201 return llvm::binary_search(IgnoredIntegerValues, Value);
202}
203
204bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
205 const llvm::APFloat FloatValue = Literal->getValue();
206 if (FloatValue.isZero())
207 return true;
208
209 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
210 const float Value = FloatValue.convertToFloat();
211 return llvm::binary_search(IgnoredFloatingPointValues, Value);
212 }
213
214 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
215 const double Value = FloatValue.convertToDouble();
216 return llvm::binary_search(IgnoredDoublePointValues, Value);
217 }
218
219 return false;
220}
221
222bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
223 const IntegerLiteral *Literal) const {
224 const std::pair<FileID, unsigned> FileOffset =
225 SourceManager->getDecomposedLoc(Literal->getLocation());
226 if (FileOffset.first.isInvalid())
227 return false;
228
229 const StringRef BufferIdentifier =
230 SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
231
232 return BufferIdentifier.empty();
233}
234
235bool MagicNumbersCheck::isBitFieldWidth(
236 const clang::ast_matchers::MatchFinder::MatchResult &Result,
237 const IntegerLiteral &Literal) const {
238 return IgnoreBitFieldsWidths &&
239 llvm::any_of(Result.Context->getParents(Literal),
240 [&Result](const DynTypedNode &Parent) {
241 return isUsedToDefineABitField(Result, Parent);
242 });
243}
244
245bool MagicNumbersCheck::isUserDefinedLiteral(
246 const clang::ast_matchers::MatchFinder::MatchResult &Result,
247 const clang::Expr &Literal) const {
248 const DynTypedNodeList Parents = Result.Context->getParents(Literal);
249 if (Parents.empty())
250 return false;
251 return Parents[0].get<UserDefinedLiteral>() != nullptr;
252}
253
254} // namespace tidy::readability
255} // namespace clang
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
const char DefaultIgnoredFloatingPointValues[]
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
llvm::StringMap< ClangTidyValue > OptionMap