clang-tools  11.0.0
MagicNumbersCheck.cpp
Go to the documentation of this file.
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  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 
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 
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 
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 
136 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 
171 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 
184 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 
204 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 
217 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
clang::tidy::readability::MagicNumbersCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: MagicNumbersCheck.cpp:128
clang::isUsedToInitializeAConstant
static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
Definition: MagicNumbersCheck.cpp:26
clang::tidy::readability::DefaultIgnoredFloatingPointValues
const char DefaultIgnoredFloatingPointValues[]
Definition: MagicNumbersCheck.cpp:62
clang::tidy::readability::DefaultIgnoredIntegerValues
const char DefaultIgnoredIntegerValues[]
Definition: MagicNumbersCheck.cpp:61
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::isUsedToDefineABitField
static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
Definition: MagicNumbersCheck.cpp:46
clang::tidy::utils::options::parseStringList
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:18
clang::tidy::readability::MagicNumbersCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: MagicNumbersCheck.cpp:111
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::readability::MagicNumbersCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: MagicNumbersCheck.cpp:122
Parent
const Node * Parent
Definition: ExtractFunction.cpp:148
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
MagicNumbersCheck.h
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:152
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111