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