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