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