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