blob: 39aaf89901f2a9dfbc2fa3f9f1d24c91fb8be2cd [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/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/STLExtras.h"
#include <algorithm>
using namespace clang::ast_matchers;
using namespace clang::ast_type_traits;
namespace {
bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result,
const DynTypedNode &Node) {
const auto *AsDecl = Node.get<clang::DeclaratorDecl>();
if (AsDecl) {
if (AsDecl->getType().isConstQualified())
return true;
return AsDecl->isImplicit();
}
if (Node.get<clang::EnumConstantDecl>() != nullptr)
return true;
return llvm::any_of(Result.Context->getParents(Node),
[&Result](const DynTypedNode &Parent) {
return isUsedToInitializeAConstant(Result, Parent);
});
}
} // namespace
namespace clang {
namespace tidy {
namespace 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)),
IgnorePowersOf2IntegerValues(
Options.get("IgnorePowersOf2IntegerValues", false)) {
// Process the set of ignored integer values.
const std::vector<std::string> IgnoredIntegerValuesInput =
utils::options::parseStringList(
Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues));
IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size());
llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(),
[](const std::string &Value) { return std::stoll(Value); });
llvm::sort(IgnoredIntegerValues);
if (!IgnoreAllFloatingPointValues) {
// Process the set of ignored floating point values.
const std::vector<std::string> IgnoredFloatingPointValuesInput =
utils::options::parseStringList(Options.get(
"IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues));
IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size());
IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size());
for (const auto &InputValue : IgnoredFloatingPointValuesInput) {
llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle());
FloatValue.convertFromString(InputValue, DefaultRoundingMode);
IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat());
llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble());
DoubleValue.convertFromString(InputValue, DefaultRoundingMode);
IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble());
}
llvm::sort(IgnoredFloatingPointValues.begin(),
IgnoredFloatingPointValues.end());
llvm::sort(IgnoredDoublePointValues.begin(),
IgnoredDoublePointValues.end());
}
}
void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoredIntegerValues", DefaultIgnoredIntegerValues);
Options.store(Opts, "IgnoredFloatingPointValues",
DefaultIgnoredFloatingPointValues);
}
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) {
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),
[&Result](const DynTypedNode &Parent) {
return isUsedToInitializeAConstant(Result, Parent) ||
// Ignore this instance, because this match reports the location
// where the template is defined, not where it is instantiated.
Parent.get<SubstNonTypeTemplateParmExpr>();
});
}
bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const {
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->getBuffer(FileOffset.first)->getBufferIdentifier();
return BufferIdentifier.empty();
}
} // namespace readability
} // namespace tidy
} // namespace clang