1 //===--- MagicNumbersCheck.cpp - clang-tidy-------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // A checker for magic numbers: integer or floating point literals embedded 11 // in the code, outside the definition of a constant or an enumeration. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "MagicNumbersCheck.h" 16 #include "../utils/OptionsUtils.h" 17 #include "clang/AST/ASTContext.h" 18 #include "clang/ASTMatchers/ASTMatchFinder.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include <algorithm> 21 22 using namespace clang::ast_matchers; 23 using namespace clang::ast_type_traits; 24 25 namespace { 26 27 bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result, 28 const DynTypedNode &Node) { 29 30 const auto *AsDecl = Node.get<clang::DeclaratorDecl>(); 31 if (AsDecl) { 32 if (AsDecl->getType().isConstQualified()) 33 return true; 34 35 return AsDecl->isImplicit(); 36 } 37 38 if (Node.get<clang::EnumConstantDecl>() != nullptr) 39 return true; 40 41 return llvm::any_of(Result.Context->getParents(Node), 42 [&Result](const DynTypedNode &Parent) { 43 return isUsedToInitializeAConstant(Result, Parent); 44 }); 45 } 46 47 } // namespace 48 49 namespace clang { 50 namespace tidy { 51 namespace readability { 52 53 const char DefaultIgnoredIntegerValues[] = "1;2;3;4;"; 54 const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;"; 55 56 MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context) 57 : ClangTidyCheck(Name, Context), 58 IgnoreAllFloatingPointValues( 59 Options.get("IgnoreAllFloatingPointValues", false)), 60 IgnorePowersOf2IntegerValues( 61 Options.get("IgnorePowersOf2IntegerValues", false)) { 62 // Process the set of ignored integer values. 63 const std::vector<std::string> IgnoredIntegerValuesInput = 64 utils::options::parseStringList( 65 Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)); 66 IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size()); 67 llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(), 68 [](const std::string &Value) { return std::stoll(Value); }); 69 llvm::sort(IgnoredIntegerValues); 70 71 if (!IgnoreAllFloatingPointValues) { 72 // Process the set of ignored floating point values. 73 const std::vector<std::string> IgnoredFloatingPointValuesInput = 74 utils::options::parseStringList(Options.get( 75 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)); 76 IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size()); 77 IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size()); 78 for (const auto &InputValue : IgnoredFloatingPointValuesInput) { 79 llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle()); 80 FloatValue.convertFromString(InputValue, DefaultRoundingMode); 81 IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat()); 82 83 llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble()); 84 DoubleValue.convertFromString(InputValue, DefaultRoundingMode); 85 IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble()); 86 } 87 llvm::sort(IgnoredFloatingPointValues.begin(), 88 IgnoredFloatingPointValues.end()); 89 llvm::sort(IgnoredDoublePointValues.begin(), 90 IgnoredDoublePointValues.end()); 91 } 92 } 93 94 void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 95 Options.store(Opts, "IgnoredIntegerValues", DefaultIgnoredIntegerValues); 96 Options.store(Opts, "IgnoredFloatingPointValues", 97 DefaultIgnoredFloatingPointValues); 98 } 99 100 void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) { 101 Finder->addMatcher(integerLiteral().bind("integer"), this); 102 if (!IgnoreAllFloatingPointValues) 103 Finder->addMatcher(floatLiteral().bind("float"), this); 104 } 105 106 void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) { 107 checkBoundMatch<IntegerLiteral>(Result, "integer"); 108 checkBoundMatch<FloatingLiteral>(Result, "float"); 109 } 110 111 bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result, 112 const Expr &ExprResult) const { 113 return llvm::any_of( 114 Result.Context->getParents(ExprResult), 115 [&Result](const DynTypedNode &Parent) { 116 return isUsedToInitializeAConstant(Result, Parent) || 117 // Ignore this instance, because this match reports the location 118 // where the template is defined, not where it is instantiated. 119 Parent.get<SubstNonTypeTemplateParmExpr>(); 120 }); 121 } 122 123 bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const { 124 const llvm::APInt IntValue = Literal->getValue(); 125 const int64_t Value = IntValue.getZExtValue(); 126 if (Value == 0) 127 return true; 128 129 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2()) 130 return true; 131 132 return std::binary_search(IgnoredIntegerValues.begin(), 133 IgnoredIntegerValues.end(), Value); 134 } 135 136 bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const { 137 const llvm::APFloat FloatValue = Literal->getValue(); 138 if (FloatValue.isZero()) 139 return true; 140 141 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) { 142 const float Value = FloatValue.convertToFloat(); 143 return std::binary_search(IgnoredFloatingPointValues.begin(), 144 IgnoredFloatingPointValues.end(), Value); 145 } 146 147 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) { 148 const double Value = FloatValue.convertToDouble(); 149 return std::binary_search(IgnoredDoublePointValues.begin(), 150 IgnoredDoublePointValues.end(), Value); 151 } 152 153 return false; 154 } 155 156 bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager, 157 const IntegerLiteral *Literal) const { 158 const std::pair<FileID, unsigned> FileOffset = 159 SourceManager->getDecomposedLoc(Literal->getLocation()); 160 if (FileOffset.first.isInvalid()) 161 return false; 162 163 const StringRef BufferIdentifier = 164 SourceManager->getBuffer(FileOffset.first)->getBufferIdentifier(); 165 166 return BufferIdentifier.empty(); 167 } 168 169 } // namespace readability 170 } // namespace tidy 171 } // namespace clang 172