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->getBuffer(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