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