clang-tools 20.0.0git
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/AST/ASTTypeTraits.h"
18#include "clang/AST/Type.h"
19#include "clang/ASTMatchers/ASTMatchFinder.h"
20#include "llvm/ADT/STLExtras.h"
21#include <algorithm>
22
23using namespace clang::ast_matchers;
24
25namespace clang {
26
27static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
28 const DynTypedNode &Node) {
29
30 const auto *AsDecl = Node.get<DeclaratorDecl>();
31 if (AsDecl) {
32 if (AsDecl->getType().isConstQualified())
33 return true;
34
35 return AsDecl->isImplicit();
36 }
37
38 if (Node.get<EnumConstantDecl>())
39 return true;
40
41 return llvm::any_of(Result.Context->getParents(Node),
42 [&Result](const DynTypedNode &Parent) {
43 return isUsedToInitializeAConstant(Result, Parent);
44 });
45}
46
47static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result,
48 const DynTypedNode &Node) {
49
50 if (Node.get<TypeAliasDecl>() || Node.get<TypedefNameDecl>())
51 return true;
52
53 return llvm::any_of(Result.Context->getParents(Node),
54 [&Result](const DynTypedNode &Parent) {
55 return isUsedToDefineATypeAlias(Result, Parent);
56 });
57}
58
59static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,
60 const DynTypedNode &Node) {
61 const auto *AsFieldDecl = Node.get<FieldDecl>();
62 if (AsFieldDecl && AsFieldDecl->isBitField())
63 return true;
64
65 return llvm::any_of(Result.Context->getParents(Node),
66 [&Result](const DynTypedNode &Parent) {
67 return isUsedToDefineABitField(Result, Parent);
68 });
69}
70
71namespace tidy::readability {
72
73const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";
74const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";
75
77 : ClangTidyCheck(Name, Context),
78 IgnoreAllFloatingPointValues(
79 Options.get("IgnoreAllFloatingPointValues", false)),
80 IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),
81 IgnorePowersOf2IntegerValues(
82 Options.get("IgnorePowersOf2IntegerValues", false)),
83 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)),
84 IgnoreUserDefinedLiterals(
85 Options.get("IgnoreUserDefinedLiterals", false)),
86 RawIgnoredIntegerValues(
87 Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),
88 RawIgnoredFloatingPointValues(Options.get(
89 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {
90 // Process the set of ignored integer values.
91 const std::vector<StringRef> IgnoredIntegerValuesInput =
92 utils::options::parseStringList(RawIgnoredIntegerValues);
93 IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
94 llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
95 [](StringRef Value) {
96 int64_t Res = 0;
97 Value.getAsInteger(10, Res);
98 return Res;
99 });
100 llvm::sort(IgnoredIntegerValues);
101
102 if (!IgnoreAllFloatingPointValues) {
103 // Process the set of ignored floating point values.
104 const std::vector<StringRef> IgnoredFloatingPointValuesInput =
105 utils::options::parseStringList(RawIgnoredFloatingPointValues);
106 IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
107 IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
108 for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
109 llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
110 auto StatusOrErr =
111 FloatValue.convertFromString(InputValue, DefaultRoundingMode);
112 assert(StatusOrErr && "Invalid floating point representation");
113 consumeError(StatusOrErr.takeError());
114 IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
115
116 llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
117 StatusOrErr =
118 DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
119 assert(StatusOrErr && "Invalid floating point representation");
120 consumeError(StatusOrErr.takeError());
121 IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
122 }
123 llvm::sort(IgnoredFloatingPointValues);
124 llvm::sort(IgnoredDoublePointValues);
125 }
126}
127
129 Options.store(Opts, "IgnoreAllFloatingPointValues",
130 IgnoreAllFloatingPointValues);
131 Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
132 Options.store(Opts, "IgnorePowersOf2IntegerValues",
133 IgnorePowersOf2IntegerValues);
134 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
135 Options.store(Opts, "IgnoreUserDefinedLiterals", IgnoreUserDefinedLiterals);
136 Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
137 Options.store(Opts, "IgnoredFloatingPointValues",
138 RawIgnoredFloatingPointValues);
139}
140
141void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
142 Finder->addMatcher(integerLiteral().bind("integer"), this);
143 if (!IgnoreAllFloatingPointValues)
144 Finder->addMatcher(floatLiteral().bind("float"), this);
145}
146
147void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
148
149 TraversalKindScope RAII(*Result.Context, TK_AsIs);
150
151 checkBoundMatch<IntegerLiteral>(Result, "integer");
152 checkBoundMatch<FloatingLiteral>(Result, "float");
153}
154
155bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
156 const Expr &ExprResult) const {
157 return llvm::any_of(
158 Result.Context->getParents(ExprResult),
159 [this, &Result](const DynTypedNode &Parent) {
160 if (isUsedToInitializeAConstant(Result, Parent))
161 return true;
162
163 if (IgnoreTypeAliases && isUsedToDefineATypeAlias(Result, Parent))
164 return true;
165
166 // Ignore this instance, because this matches an
167 // expanded class enumeration value.
168 if (Parent.get<CStyleCastExpr>() &&
169 llvm::any_of(
170 Result.Context->getParents(Parent),
171 [](const DynTypedNode &GrandParent) {
172 return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
173 nullptr;
174 }))
175 return true;
176
177 // Ignore this instance, because this match reports the
178 // location where the template is defined, not where it
179 // is instantiated.
180 if (Parent.get<SubstNonTypeTemplateParmExpr>())
181 return true;
182
183 // Don't warn on string user defined literals:
184 // std::string s = "Hello World"s;
185 if (const auto *UDL = Parent.get<UserDefinedLiteral>())
186 if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
187 return true;
188
189 return false;
190 });
191}
192
193bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
194 if (Literal->getType()->isBitIntType()) {
195 return true;
196 }
197 const llvm::APInt IntValue = Literal->getValue();
198 const int64_t Value = IntValue.getZExtValue();
199 if (Value == 0)
200 return true;
201
202 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
203 return true;
204
205 return std::binary_search(IgnoredIntegerValues.begin(),
206 IgnoredIntegerValues.end(), Value);
207}
208
209bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
210 const llvm::APFloat FloatValue = Literal->getValue();
211 if (FloatValue.isZero())
212 return true;
213
214 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
215 const float Value = FloatValue.convertToFloat();
216 return std::binary_search(IgnoredFloatingPointValues.begin(),
217 IgnoredFloatingPointValues.end(), Value);
218 }
219
220 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
221 const double Value = FloatValue.convertToDouble();
222 return std::binary_search(IgnoredDoublePointValues.begin(),
223 IgnoredDoublePointValues.end(), Value);
224 }
225
226 return false;
227}
228
229bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
230 const IntegerLiteral *Literal) const {
231 const std::pair<FileID, unsigned> FileOffset =
232 SourceManager->getDecomposedLoc(Literal->getLocation());
233 if (FileOffset.first.isInvalid())
234 return false;
235
236 const StringRef BufferIdentifier =
237 SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
238
239 return BufferIdentifier.empty();
240}
241
242bool MagicNumbersCheck::isBitFieldWidth(
243 const clang::ast_matchers::MatchFinder::MatchResult &Result,
244 const IntegerLiteral &Literal) const {
245 return IgnoreBitFieldsWidths &&
246 llvm::any_of(Result.Context->getParents(Literal),
247 [&Result](const DynTypedNode &Parent) {
248 return isUsedToDefineABitField(Result, Parent);
249 });
250}
251
252bool MagicNumbersCheck::isUserDefinedLiteral(
253 const clang::ast_matchers::MatchFinder::MatchResult &Result,
254 const clang::Expr &Literal) const {
255 DynTypedNodeList Parents = Result.Context->getParents(Literal);
256 if (Parents.empty())
257 return false;
258 return Parents[0].get<UserDefinedLiteral>() != nullptr;
259}
260
261} // namespace tidy::readability
262} // namespace clang
llvm::SmallString< 256U > Name
const Node * Parent
::clang::DynTypedNode Node
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.
Base class for all clang-tidy checks.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
const char DefaultIgnoredIntegerValues[]
const char DefaultIgnoredFloatingPointValues[]
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result, const DynTypedNode &Node)
llvm::StringMap< ClangTidyValue > OptionMap