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 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 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 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 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 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 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 135 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 170 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 183 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 203 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 216 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