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/ASTMatchers/ASTMatchFinder.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include <algorithm>
20
21 using namespace clang::ast_matchers;
22
23 namespace clang {
24
isUsedToInitializeAConstant(const MatchFinder::MatchResult & Result,const DynTypedNode & Node)25 static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
26 const DynTypedNode &Node) {
27
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
isUsedToDefineABitField(const MatchFinder::MatchResult & Result,const DynTypedNode & Node)45 static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,
46 const DynTypedNode &Node) {
47 const auto *AsFieldDecl = Node.get<FieldDecl>();
48 if (AsFieldDecl && AsFieldDecl->isBitField())
49 return true;
50
51 return llvm::any_of(Result.Context->getParents(Node),
52 [&Result](const DynTypedNode &Parent) {
53 return isUsedToDefineABitField(Result, Parent);
54 });
55 }
56
57 namespace tidy {
58 namespace readability {
59
60 const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";
61 const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";
62
MagicNumbersCheck(StringRef Name,ClangTidyContext * Context)63 MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)
64 : ClangTidyCheck(Name, Context),
65 IgnoreAllFloatingPointValues(
66 Options.get("IgnoreAllFloatingPointValues", false)),
67 IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),
68 IgnorePowersOf2IntegerValues(
69 Options.get("IgnorePowersOf2IntegerValues", false)),
70 RawIgnoredIntegerValues(
71 Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),
72 RawIgnoredFloatingPointValues(Options.get(
73 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {
74 // Process the set of ignored integer values.
75 const std::vector<std::string> IgnoredIntegerValuesInput =
76 utils::options::parseStringList(RawIgnoredIntegerValues);
77 IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
78 llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
79 [](const std::string &Value) { return std::stoll(Value); });
80 llvm::sort(IgnoredIntegerValues);
81
82 if (!IgnoreAllFloatingPointValues) {
83 // Process the set of ignored floating point values.
84 const std::vector<std::string> IgnoredFloatingPointValuesInput =
85 utils::options::parseStringList(RawIgnoredFloatingPointValues);
86 IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
87 IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
88 for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
89 llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
90 auto StatusOrErr =
91 FloatValue.convertFromString(InputValue, DefaultRoundingMode);
92 assert(StatusOrErr && "Invalid floating point representation");
93 consumeError(StatusOrErr.takeError());
94 IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
95
96 llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
97 StatusOrErr =
98 DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
99 assert(StatusOrErr && "Invalid floating point representation");
100 consumeError(StatusOrErr.takeError());
101 IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
102 }
103 llvm::sort(IgnoredFloatingPointValues.begin(),
104 IgnoredFloatingPointValues.end());
105 llvm::sort(IgnoredDoublePointValues.begin(),
106 IgnoredDoublePointValues.end());
107 }
108 }
109
storeOptions(ClangTidyOptions::OptionMap & Opts)110 void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
111 Options.store(Opts, "IgnoreAllFloatingPointValues",
112 IgnoreAllFloatingPointValues);
113 Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
114 Options.store(Opts, "IgnorePowersOf2IntegerValues",
115 IgnorePowersOf2IntegerValues);
116 Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
117 Options.store(Opts, "IgnoredFloatingPointValues",
118 RawIgnoredFloatingPointValues);
119 }
120
registerMatchers(MatchFinder * Finder)121 void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
122 Finder->addMatcher(integerLiteral().bind("integer"), this);
123 if (!IgnoreAllFloatingPointValues)
124 Finder->addMatcher(floatLiteral().bind("float"), this);
125 }
126
check(const MatchFinder::MatchResult & Result)127 void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
128
129 TraversalKindScope RAII(*Result.Context, TK_AsIs);
130
131 checkBoundMatch<IntegerLiteral>(Result, "integer");
132 checkBoundMatch<FloatingLiteral>(Result, "float");
133 }
134
isConstant(const MatchFinder::MatchResult & Result,const Expr & ExprResult) const135 bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
136 const Expr &ExprResult) const {
137 return llvm::any_of(
138 Result.Context->getParents(ExprResult),
139 [&Result](const DynTypedNode &Parent) {
140 if (isUsedToInitializeAConstant(Result, Parent))
141 return true;
142
143 // Ignore this instance, because this matches an
144 // expanded class enumeration value.
145 if (Parent.get<CStyleCastExpr>() &&
146 llvm::any_of(
147 Result.Context->getParents(Parent),
148 [](const DynTypedNode &GrandParent) {
149 return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
150 nullptr;
151 }))
152 return true;
153
154 // Ignore this instance, because this match reports the
155 // location where the template is defined, not where it
156 // is instantiated.
157 if (Parent.get<SubstNonTypeTemplateParmExpr>())
158 return true;
159
160 // Don't warn on string user defined literals:
161 // std::string s = "Hello World"s;
162 if (const auto *UDL = Parent.get<UserDefinedLiteral>())
163 if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
164 return true;
165
166 return false;
167 });
168 }
169
isIgnoredValue(const IntegerLiteral * Literal) const170 bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
171 const llvm::APInt IntValue = Literal->getValue();
172 const int64_t Value = IntValue.getZExtValue();
173 if (Value == 0)
174 return true;
175
176 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
177 return true;
178
179 return std::binary_search(IgnoredIntegerValues.begin(),
180 IgnoredIntegerValues.end(), Value);
181 }
182
isIgnoredValue(const FloatingLiteral * Literal) const183 bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
184 const llvm::APFloat FloatValue = Literal->getValue();
185 if (FloatValue.isZero())
186 return true;
187
188 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
189 const float Value = FloatValue.convertToFloat();
190 return std::binary_search(IgnoredFloatingPointValues.begin(),
191 IgnoredFloatingPointValues.end(), Value);
192 }
193
194 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
195 const double Value = FloatValue.convertToDouble();
196 return std::binary_search(IgnoredDoublePointValues.begin(),
197 IgnoredDoublePointValues.end(), Value);
198 }
199
200 return false;
201 }
202
isSyntheticValue(const SourceManager * SourceManager,const IntegerLiteral * Literal) const203 bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
204 const IntegerLiteral *Literal) const {
205 const std::pair<FileID, unsigned> FileOffset =
206 SourceManager->getDecomposedLoc(Literal->getLocation());
207 if (FileOffset.first.isInvalid())
208 return false;
209
210 const StringRef BufferIdentifier =
211 SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
212
213 return BufferIdentifier.empty();
214 }
215
isBitFieldWidth(const clang::ast_matchers::MatchFinder::MatchResult & Result,const IntegerLiteral & Literal) const216 bool MagicNumbersCheck::isBitFieldWidth(
217 const clang::ast_matchers::MatchFinder::MatchResult &Result,
218 const IntegerLiteral &Literal) const {
219 return IgnoreBitFieldsWidths &&
220 llvm::any_of(Result.Context->getParents(Literal),
221 [&Result](const DynTypedNode &Parent) {
222 return isUsedToDefineABitField(Result, Parent);
223 });
224 }
225
226 } // namespace readability
227 } // namespace tidy
228 } // namespace clang
229