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