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 
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 
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 
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.begin(),
108                IgnoredFloatingPointValues.end());
109     llvm::sort(IgnoredDoublePointValues.begin(),
110                IgnoredDoublePointValues.end());
111   }
112 }
113 
114 void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
115   Options.store(Opts, "IgnoreAllFloatingPointValues",
116                 IgnoreAllFloatingPointValues);
117   Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
118   Options.store(Opts, "IgnorePowersOf2IntegerValues",
119                 IgnorePowersOf2IntegerValues);
120   Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
121   Options.store(Opts, "IgnoredFloatingPointValues",
122                 RawIgnoredFloatingPointValues);
123 }
124 
125 void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
126   Finder->addMatcher(integerLiteral().bind("integer"), this);
127   if (!IgnoreAllFloatingPointValues)
128     Finder->addMatcher(floatLiteral().bind("float"), this);
129 }
130 
131 void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
132 
133   TraversalKindScope RAII(*Result.Context, TK_AsIs);
134 
135   checkBoundMatch<IntegerLiteral>(Result, "integer");
136   checkBoundMatch<FloatingLiteral>(Result, "float");
137 }
138 
139 bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
140                                    const Expr &ExprResult) const {
141   return llvm::any_of(
142       Result.Context->getParents(ExprResult),
143       [&Result](const DynTypedNode &Parent) {
144         if (isUsedToInitializeAConstant(Result, Parent))
145           return true;
146 
147         // Ignore this instance, because this matches an
148         // expanded class enumeration value.
149         if (Parent.get<CStyleCastExpr>() &&
150             llvm::any_of(
151                 Result.Context->getParents(Parent),
152                 [](const DynTypedNode &GrandParent) {
153                   return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
154                          nullptr;
155                 }))
156           return true;
157 
158         // Ignore this instance, because this match reports the
159         // location where the template is defined, not where it
160         // is instantiated.
161         if (Parent.get<SubstNonTypeTemplateParmExpr>())
162           return true;
163 
164         // Don't warn on string user defined literals:
165         // std::string s = "Hello World"s;
166         if (const auto *UDL = Parent.get<UserDefinedLiteral>())
167           if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
168             return true;
169 
170         return false;
171       });
172 }
173 
174 bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
175   const llvm::APInt IntValue = Literal->getValue();
176   const int64_t Value = IntValue.getZExtValue();
177   if (Value == 0)
178     return true;
179 
180   if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
181     return true;
182 
183   return std::binary_search(IgnoredIntegerValues.begin(),
184                             IgnoredIntegerValues.end(), Value);
185 }
186 
187 bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
188   const llvm::APFloat FloatValue = Literal->getValue();
189   if (FloatValue.isZero())
190     return true;
191 
192   if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
193     const float Value = FloatValue.convertToFloat();
194     return std::binary_search(IgnoredFloatingPointValues.begin(),
195                               IgnoredFloatingPointValues.end(), Value);
196   }
197 
198   if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
199     const double Value = FloatValue.convertToDouble();
200     return std::binary_search(IgnoredDoublePointValues.begin(),
201                               IgnoredDoublePointValues.end(), Value);
202   }
203 
204   return false;
205 }
206 
207 bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
208                                          const IntegerLiteral *Literal) const {
209   const std::pair<FileID, unsigned> FileOffset =
210       SourceManager->getDecomposedLoc(Literal->getLocation());
211   if (FileOffset.first.isInvalid())
212     return false;
213 
214   const StringRef BufferIdentifier =
215       SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
216 
217   return BufferIdentifier.empty();
218 }
219 
220 bool MagicNumbersCheck::isBitFieldWidth(
221     const clang::ast_matchers::MatchFinder::MatchResult &Result,
222     const IntegerLiteral &Literal) const {
223   return IgnoreBitFieldsWidths &&
224          llvm::any_of(Result.Context->getParents(Literal),
225                       [&Result](const DynTypedNode &Parent) {
226                         return isUsedToDefineABitField(Result, Parent);
227                       });
228 }
229 
230 } // namespace readability
231 } // namespace tidy
232 } // namespace clang
233