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