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