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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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