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