256 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// A checker for magic numbers: integer or floating point literals embedded10// 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/AST/ASTTypeTraits.h"18#include "clang/AST/Type.h"19#include "clang/ASTMatchers/ASTMatchFinder.h"20#include "llvm/ADT/STLExtras.h"21 22using namespace clang::ast_matchers;23 24namespace clang {25 26static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,27 const DynTypedNode &Node) {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 45static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result,46 const DynTypedNode &Node) {47 if (Node.get<TypeAliasDecl>() || Node.get<TypedefNameDecl>())48 return true;49 50 return llvm::any_of(Result.Context->getParents(Node),51 [&Result](const DynTypedNode &Parent) {52 return isUsedToDefineATypeAlias(Result, Parent);53 });54}55 56static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,57 const DynTypedNode &Node) {58 const auto *AsFieldDecl = Node.get<FieldDecl>();59 if (AsFieldDecl && AsFieldDecl->isBitField())60 return true;61 62 return llvm::any_of(Result.Context->getParents(Node),63 [&Result](const DynTypedNode &Parent) {64 return isUsedToDefineABitField(Result, Parent);65 });66}67 68namespace tidy::readability {69 70const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";71const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";72 73MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)74 : ClangTidyCheck(Name, Context),75 IgnoreAllFloatingPointValues(76 Options.get("IgnoreAllFloatingPointValues", false)),77 IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),78 IgnorePowersOf2IntegerValues(79 Options.get("IgnorePowersOf2IntegerValues", false)),80 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)),81 IgnoreUserDefinedLiterals(82 Options.get("IgnoreUserDefinedLiterals", false)),83 RawIgnoredIntegerValues(84 Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),85 RawIgnoredFloatingPointValues(Options.get(86 "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {87 // Process the set of ignored integer values.88 const std::vector<StringRef> IgnoredIntegerValuesInput =89 utils::options::parseStringList(RawIgnoredIntegerValues);90 IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());91 llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),92 [](StringRef Value) {93 int64_t Res = 0;94 Value.getAsInteger(10, Res);95 return Res;96 });97 llvm::sort(IgnoredIntegerValues);98 99 if (!IgnoreAllFloatingPointValues) {100 // Process the set of ignored floating point values.101 const std::vector<StringRef> IgnoredFloatingPointValuesInput =102 utils::options::parseStringList(RawIgnoredFloatingPointValues);103 IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());104 IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());105 for (const auto &InputValue : IgnoredFloatingPointValuesInput) {106 llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());107 auto StatusOrErr =108 FloatValue.convertFromString(InputValue, DefaultRoundingMode);109 assert(StatusOrErr && "Invalid floating point representation");110 consumeError(StatusOrErr.takeError());111 IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());112 113 llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());114 StatusOrErr =115 DoubleValue.convertFromString(InputValue, DefaultRoundingMode);116 assert(StatusOrErr && "Invalid floating point representation");117 consumeError(StatusOrErr.takeError());118 IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());119 }120 llvm::sort(IgnoredFloatingPointValues);121 llvm::sort(IgnoredDoublePointValues);122 }123}124 125void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {126 Options.store(Opts, "IgnoreAllFloatingPointValues",127 IgnoreAllFloatingPointValues);128 Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);129 Options.store(Opts, "IgnorePowersOf2IntegerValues",130 IgnorePowersOf2IntegerValues);131 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);132 Options.store(Opts, "IgnoreUserDefinedLiterals", IgnoreUserDefinedLiterals);133 Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);134 Options.store(Opts, "IgnoredFloatingPointValues",135 RawIgnoredFloatingPointValues);136}137 138void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {139 Finder->addMatcher(integerLiteral().bind("integer"), this);140 if (!IgnoreAllFloatingPointValues)141 Finder->addMatcher(floatLiteral().bind("float"), this);142}143 144void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {145 const TraversalKindScope RAII(*Result.Context, TK_AsIs);146 147 checkBoundMatch<IntegerLiteral>(Result, "integer");148 checkBoundMatch<FloatingLiteral>(Result, "float");149}150 151bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,152 const Expr &ExprResult) const {153 return llvm::any_of(154 Result.Context->getParents(ExprResult),155 [this, &Result](const DynTypedNode &Parent) {156 if (isUsedToInitializeAConstant(Result, Parent))157 return true;158 159 if (IgnoreTypeAliases && isUsedToDefineATypeAlias(Result, Parent))160 return true;161 162 // Ignore this instance, because this matches an163 // expanded class enumeration value.164 if (Parent.get<CStyleCastExpr>() &&165 llvm::any_of(166 Result.Context->getParents(Parent),167 [](const DynTypedNode &GrandParent) {168 return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=169 nullptr;170 }))171 return true;172 173 // Ignore this instance, because this match reports the174 // location where the template is defined, not where it175 // is instantiated.176 if (Parent.get<SubstNonTypeTemplateParmExpr>())177 return true;178 179 // Don't warn on string user defined literals:180 // std::string s = "Hello World"s;181 if (const auto *UDL = Parent.get<UserDefinedLiteral>())182 if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)183 return true;184 185 return false;186 });187}188 189bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {190 if (Literal->getType()->isBitIntType()) {191 return true;192 }193 const llvm::APInt IntValue = Literal->getValue();194 const int64_t Value = IntValue.getZExtValue();195 if (Value == 0)196 return true;197 198 if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())199 return true;200 201 return llvm::binary_search(IgnoredIntegerValues, Value);202}203 204bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {205 const llvm::APFloat FloatValue = Literal->getValue();206 if (FloatValue.isZero())207 return true;208 209 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {210 const float Value = FloatValue.convertToFloat();211 return llvm::binary_search(IgnoredFloatingPointValues, Value);212 }213 214 if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {215 const double Value = FloatValue.convertToDouble();216 return llvm::binary_search(IgnoredDoublePointValues, Value);217 }218 219 return false;220}221 222bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,223 const IntegerLiteral *Literal) const {224 const std::pair<FileID, unsigned> FileOffset =225 SourceManager->getDecomposedLoc(Literal->getLocation());226 if (FileOffset.first.isInvalid())227 return false;228 229 const StringRef BufferIdentifier =230 SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();231 232 return BufferIdentifier.empty();233}234 235bool MagicNumbersCheck::isBitFieldWidth(236 const clang::ast_matchers::MatchFinder::MatchResult &Result,237 const IntegerLiteral &Literal) const {238 return IgnoreBitFieldsWidths &&239 llvm::any_of(Result.Context->getParents(Literal),240 [&Result](const DynTypedNode &Parent) {241 return isUsedToDefineABitField(Result, Parent);242 });243}244 245bool MagicNumbersCheck::isUserDefinedLiteral(246 const clang::ast_matchers::MatchFinder::MatchResult &Result,247 const clang::Expr &Literal) const {248 const DynTypedNodeList Parents = Result.Context->getParents(Literal);249 if (Parents.empty())250 return false;251 return Parents[0].get<UserDefinedLiteral>() != nullptr;252}253 254} // namespace tidy::readability255} // namespace clang256