[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / clang-tools-extra / clang-tidy / readability / MagicNumbersCheck.cpp
blob97c20cf200fa21c43c38f63c20fff7b6804e9754
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.
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"
21 #include <algorithm>
23 using namespace clang::ast_matchers;
25 namespace clang {
27 static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
28 const DynTypedNode &Node) {
30 const auto *AsDecl = Node.get<DeclaratorDecl>();
31 if (AsDecl) {
32 if (AsDecl->getType().isConstQualified())
33 return true;
35 return AsDecl->isImplicit();
38 if (Node.get<EnumConstantDecl>())
39 return true;
41 return llvm::any_of(Result.Context->getParents(Node),
42 [&Result](const DynTypedNode &Parent) {
43 return isUsedToInitializeAConstant(Result, Parent);
44 });
47 static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result,
48 const DynTypedNode &Node) {
50 if (Node.get<TypeAliasDecl>() || Node.get<TypedefNameDecl>())
51 return true;
53 return llvm::any_of(Result.Context->getParents(Node),
54 [&Result](const DynTypedNode &Parent) {
55 return isUsedToDefineATypeAlias(Result, Parent);
56 });
59 static 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;
65 return llvm::any_of(Result.Context->getParents(Node),
66 [&Result](const DynTypedNode &Parent) {
67 return isUsedToDefineABitField(Result, Parent);
68 });
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(),
95 [](StringRef Value) {
96 int64_t Res = 0;
97 Value.getAsInteger(10, Res);
98 return Res;
99 });
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());
110 auto StatusOrErr =
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());
117 StatusOrErr =
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 {
157 return llvm::any_of(
158 Result.Context->getParents(ExprResult),
159 [this, &Result](const DynTypedNode &Parent) {
160 if (isUsedToInitializeAConstant(Result, Parent))
161 return true;
163 if (IgnoreTypeAliases && isUsedToDefineATypeAlias(Result, Parent))
164 return true;
166 // Ignore this instance, because this matches an
167 // expanded class enumeration value.
168 if (Parent.get<CStyleCastExpr>() &&
169 llvm::any_of(
170 Result.Context->getParents(Parent),
171 [](const DynTypedNode &GrandParent) {
172 return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
173 nullptr;
175 return true;
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;
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;
189 return false;
193 bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
194 if (Literal->getType()->isBitIntType()) {
195 return true;
197 const llvm::APInt IntValue = Literal->getValue();
198 const int64_t Value = IntValue.getZExtValue();
199 if (Value == 0)
200 return true;
202 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
203 return true;
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())
212 return true;
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);
226 return false;
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())
234 return false;
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);
256 if (Parents.empty())
257 return false;
258 return Parents[0].get<UserDefinedLiteral>() != nullptr;
261 } // namespace tidy::readability
262 } // namespace clang