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

source code of clang-tools-extra/clang-tidy/readability/MagicNumbersCheck.cpp