aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp73
1 files changed, 53 insertions, 20 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 0e94b915a468..0fa20428c1b5 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -22,9 +22,12 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <optional>
using namespace clang;
using namespace ento;
+using llvm::formatv;
namespace {
// This evaluator checks two SVals for equality. The first SVal is provided via
@@ -57,8 +60,9 @@ public:
// Being conservative, it does not warn if there is slight possibility the
// value can be matching.
class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
- mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
- void reportWarning(CheckerContext &C) const;
+ const BugType EnumValueCastOutOfRange{this, "Enum cast out of range"};
+ void reportWarning(CheckerContext &C, const CastExpr *CE,
+ const EnumDecl *E) const;
public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
@@ -71,21 +75,39 @@ EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
EnumValueVector DeclValues(
std::distance(ED->enumerator_begin(), ED->enumerator_end()));
llvm::transform(ED->enumerators(), DeclValues.begin(),
- [](const EnumConstantDecl *D) { return D->getInitVal(); });
+ [](const EnumConstantDecl *D) { return D->getInitVal(); });
return DeclValues;
}
} // namespace
-void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
+void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C,
+ const CastExpr *CE,
+ const EnumDecl *E) const {
+ assert(E && "valid EnumDecl* is expected");
if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
- if (!EnumValueCastOutOfRange)
- EnumValueCastOutOfRange.reset(
- new BuiltinBug(this, "Enum cast out of range",
- "The value provided to the cast expression is not in "
- "the valid range of values for the enum"));
- C.emitReport(std::make_unique<PathSensitiveBugReport>(
- *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
- N));
+ std::string ValueStr = "", NameStr = "the enum";
+
+ // Try to add details to the message:
+ const auto ConcreteValue =
+ C.getSVal(CE->getSubExpr()).getAs<nonloc::ConcreteInt>();
+ if (ConcreteValue) {
+ ValueStr = formatv(" '{0}'", ConcreteValue->getValue());
+ }
+ if (StringRef EnumName{E->getName()}; !EnumName.empty()) {
+ NameStr = formatv("'{0}'", EnumName);
+ }
+
+ std::string Msg = formatv("The value{0} provided to the cast expression is "
+ "not in the valid range of values for {1}",
+ ValueStr, NameStr);
+
+ auto BR = std::make_unique<PathSensitiveBugReport>(EnumValueCastOutOfRange,
+ Msg, N);
+ bugreporter::trackExpressionValue(N, CE->getSubExpr(), *BR);
+ BR->addNote("enum declared here",
+ PathDiagnosticLocation::create(E, C.getSourceManager()),
+ {E->getSourceRange()});
+ C.emitReport(std::move(BR));
}
}
@@ -94,10 +116,10 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
// Only perform enum range check on casts where such checks are valid. For
// all other cast kinds (where enum range checks are unnecessary or invalid),
- // just return immediately. TODO: The set of casts whitelisted for enum
- // range checking may be incomplete. Better to add a missing cast kind to
- // enable a missing check than to generate false negatives and have to remove
- // those later.
+ // just return immediately. TODO: The set of casts allowed for enum range
+ // checking may be incomplete. Better to add a missing cast kind to enable a
+ // missing check than to generate false negatives and have to remove those
+ // later.
switch (CE->getCastKind()) {
case CK_IntegralCast:
break;
@@ -108,7 +130,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
}
// Get the value of the expression to cast.
- const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
+ const std::optional<DefinedOrUnknownSVal> ValueToCast =
C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
// If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
@@ -128,14 +150,25 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
EnumValueVector DeclValues = getDeclValuesForEnum(ED);
+
+ // If the declarator list is empty, bail out.
+ // Every initialization an enum with a fixed underlying type but without any
+ // enumerators would produce a warning if we were to continue at this point.
+ // The most notable example is std::byte in the C++17 standard library.
+ // TODO: Create heuristics to bail out when the enum type is intended to be
+ // used to store combinations of flag values (to mitigate the limitation
+ // described in the docs).
+ if (DeclValues.size() == 0)
+ return;
+
// Check if any of the enum values possibly match.
- bool PossibleValueMatch = llvm::any_of(
- DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
+ bool PossibleValueMatch =
+ llvm::any_of(DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
// If there is no value that can possibly match any of the enum values, then
// warn.
if (!PossibleValueMatch)
- reportWarning(C);
+ reportWarning(C, CE, ED);
}
void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {