blob: 97c20cf200fa21c43c38f63c20fff7b6804e9754 [file] [log] [blame]
//===--- MagicNumbersCheck.cpp - clang-tidy-------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// A checker for magic numbers: integer or floating point literals embedded
// in the code, outside the definition of a constant or an enumeration.
//
//===----------------------------------------------------------------------===//
#include "MagicNumbersCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/STLExtras.h"
#include <algorithm>
using namespace clang::ast_matchers;
namespace clang {
static bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
const DynTypedNode &Node) {
const auto *AsDecl = Node.get<DeclaratorDecl>();
if (AsDecl) {
if (AsDecl->getType().isConstQualified())
return true;
return AsDecl->isImplicit();
}
if (Node.get<EnumConstantDecl>())
return true;
return llvm::any_of(Result.Context->getParents(Node),
[&Result](const DynTypedNode &Parent) {
return isUsedToInitializeAConstant(Result, Parent);
});
}
static bool isUsedToDefineATypeAlias(const MatchFinder::MatchResult &Result,
const DynTypedNode &Node) {
if (Node.get<TypeAliasDecl>() || Node.get<TypedefNameDecl>())
return true;
return llvm::any_of(Result.Context->getParents(Node),
[&Result](const DynTypedNode &Parent) {
return isUsedToDefineATypeAlias(Result, Parent);
});
}
static bool isUsedToDefineABitField(const MatchFinder::MatchResult &Result,
const DynTypedNode &Node) {
const auto *AsFieldDecl = Node.get<FieldDecl>();
if (AsFieldDecl && AsFieldDecl->isBitField())
return true;
return llvm::any_of(Result.Context->getParents(Node),
[&Result](const DynTypedNode &Parent) {
return isUsedToDefineABitField(Result, Parent);
});
}
namespace tidy::readability {
const char DefaultIgnoredIntegerValues[] = "1;2;3;4;";
const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;";
MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoreAllFloatingPointValues(
Options.get("IgnoreAllFloatingPointValues", false)),
IgnoreBitFieldsWidths(Options.get("IgnoreBitFieldsWidths", true)),
IgnorePowersOf2IntegerValues(
Options.get("IgnorePowersOf2IntegerValues", false)),
IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)),
IgnoreUserDefinedLiterals(
Options.get("IgnoreUserDefinedLiterals", false)),
RawIgnoredIntegerValues(
Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)),
RawIgnoredFloatingPointValues(Options.get(
"IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)) {
// Process the set of ignored integer values.
const std::vector<StringRef> IgnoredIntegerValuesInput =
utils::options::parseStringList(RawIgnoredIntegerValues);
IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
[](StringRef Value) {
int64_t Res = 0;
Value.getAsInteger(10, Res);
return Res;
});
llvm::sort(IgnoredIntegerValues);
if (!IgnoreAllFloatingPointValues) {
// Process the set of ignored floating point values.
const std::vector<StringRef> IgnoredFloatingPointValuesInput =
utils::options::parseStringList(RawIgnoredFloatingPointValues);
IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
auto StatusOrErr =
FloatValue.convertFromString(InputValue, DefaultRoundingMode);
assert(StatusOrErr && "Invalid floating point representation");
consumeError(StatusOrErr.takeError());
IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
StatusOrErr =
DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
assert(StatusOrErr && "Invalid floating point representation");
consumeError(StatusOrErr.takeError());
IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
}
llvm::sort(IgnoredFloatingPointValues);
llvm::sort(IgnoredDoublePointValues);
}
}
void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoreAllFloatingPointValues",
IgnoreAllFloatingPointValues);
Options.store(Opts, "IgnoreBitFieldsWidths", IgnoreBitFieldsWidths);
Options.store(Opts, "IgnorePowersOf2IntegerValues",
IgnorePowersOf2IntegerValues);
Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);
Options.store(Opts, "IgnoreUserDefinedLiterals", IgnoreUserDefinedLiterals);
Options.store(Opts, "IgnoredIntegerValues", RawIgnoredIntegerValues);
Options.store(Opts, "IgnoredFloatingPointValues",
RawIgnoredFloatingPointValues);
}
void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(integerLiteral().bind("integer"), this);
if (!IgnoreAllFloatingPointValues)
Finder->addMatcher(floatLiteral().bind("float"), this);
}
void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
TraversalKindScope RAII(*Result.Context, TK_AsIs);
checkBoundMatch<IntegerLiteral>(Result, "integer");
checkBoundMatch<FloatingLiteral>(Result, "float");
}
bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result,
const Expr &ExprResult) const {
return llvm::any_of(
Result.Context->getParents(ExprResult),
[this, &Result](const DynTypedNode &Parent) {
if (isUsedToInitializeAConstant(Result, Parent))
return true;
if (IgnoreTypeAliases && isUsedToDefineATypeAlias(Result, Parent))
return true;
// Ignore this instance, because this matches an
// expanded class enumeration value.
if (Parent.get<CStyleCastExpr>() &&
llvm::any_of(
Result.Context->getParents(Parent),
[](const DynTypedNode &GrandParent) {
return GrandParent.get<SubstNonTypeTemplateParmExpr>() !=
nullptr;
}))
return true;
// Ignore this instance, because this match reports the
// location where the template is defined, not where it
// is instantiated.
if (Parent.get<SubstNonTypeTemplateParmExpr>())
return true;
// Don't warn on string user defined literals:
// std::string s = "Hello World"s;
if (const auto *UDL = Parent.get<UserDefinedLiteral>())
if (UDL->getLiteralOperatorKind() == UserDefinedLiteral::LOK_String)
return true;
return false;
});
}
bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
if (Literal->getType()->isBitIntType()) {
return true;
}
const llvm::APInt IntValue = Literal->getValue();
const int64_t Value = IntValue.getZExtValue();
if (Value == 0)
return true;
if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2())
return true;
return std::binary_search(IgnoredIntegerValues.begin(),
IgnoredIntegerValues.end(), Value);
}
bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const {
const llvm::APFloat FloatValue = Literal->getValue();
if (FloatValue.isZero())
return true;
if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) {
const float Value = FloatValue.convertToFloat();
return std::binary_search(IgnoredFloatingPointValues.begin(),
IgnoredFloatingPointValues.end(), Value);
}
if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) {
const double Value = FloatValue.convertToDouble();
return std::binary_search(IgnoredDoublePointValues.begin(),
IgnoredDoublePointValues.end(), Value);
}
return false;
}
bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager,
const IntegerLiteral *Literal) const {
const std::pair<FileID, unsigned> FileOffset =
SourceManager->getDecomposedLoc(Literal->getLocation());
if (FileOffset.first.isInvalid())
return false;
const StringRef BufferIdentifier =
SourceManager->getBufferOrFake(FileOffset.first).getBufferIdentifier();
return BufferIdentifier.empty();
}
bool MagicNumbersCheck::isBitFieldWidth(
const clang::ast_matchers::MatchFinder::MatchResult &Result,
const IntegerLiteral &Literal) const {
return IgnoreBitFieldsWidths &&
llvm::any_of(Result.Context->getParents(Literal),
[&Result](const DynTypedNode &Parent) {
return isUsedToDefineABitField(Result, Parent);
});
}
bool MagicNumbersCheck::isUserDefinedLiteral(
const clang::ast_matchers::MatchFinder::MatchResult &Result,
const clang::Expr &Literal) const {
DynTypedNodeList Parents = Result.Context->getParents(Literal);
if (Parents.empty())
return false;
return Parents[0].get<UserDefinedLiteral>() != nullptr;
}
} // namespace tidy::readability
} // namespace clang