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