Skip to content

Commit

Permalink
[analyzer] Extend EnumCastOutOfRange diagnostics (#68191)
Browse files Browse the repository at this point in the history
EnumCastOutOfRange checker now reports the name of the enum in the
warning message. Additionally, a note-tag is placed to highlight the
location of the declaration.
  • Loading branch information
Endre Fülöp committed Oct 28, 2023
1 parent 6ed2d30 commit 8db38bd
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 69 deletions.
40 changes: 29 additions & 11 deletions clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ class ConstraintBasedEQEvaluator {
// value can be matching.
class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
mutable std::unique_ptr<BugType> EnumValueCastOutOfRange;
void reportWarning(CheckerContext &C) const;
void reportWarning(CheckerContext &C, const CastExpr *CE,
const EnumDecl *E) const;

public:
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
Expand All @@ -72,21 +73,38 @@ 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 BugType(this, "Enum cast out of range"));
constexpr llvm::StringLiteral Msg =
"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, Msg, N));

llvm::SmallString<128> Msg{"The value provided to the cast expression is "
"not in the valid range of values for "};
StringRef EnumName{E->getName()};
if (EnumName.empty()) {
Msg += "the enum";
} else {
Msg += '\'';
Msg += EnumName;
Msg += '\'';
}

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));
}
}

Expand Down Expand Up @@ -138,13 +156,13 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
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) {
Expand Down
44 changes: 38 additions & 6 deletions clang/test/Analysis/enum-cast-out-of-range.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
// RUN: -analyzer-output text \
// RUN: -verify %s

// expected-note@+1 + {{enum declared here}}
enum En_t {
En_0 = -4,
En_1,
Expand All @@ -11,17 +13,23 @@ enum En_t {
};

void unscopedUnspecifiedCStyle(void) {
enum En_t Below = (enum En_t)(-5); // expected-warning {{not in the valid range}}
enum En_t Below = (enum En_t)(-5); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
enum En_t NegVal1 = (enum En_t)(-4); // OK.
enum En_t NegVal2 = (enum En_t)(-3); // OK.
enum En_t InRange1 = (enum En_t)(-2); // expected-warning {{not in the valid range}}
enum En_t InRange2 = (enum En_t)(-1); // expected-warning {{not in the valid range}}
enum En_t InRange3 = (enum En_t)(0); // expected-warning {{not in the valid range}}
enum En_t InRange1 = (enum En_t)(-2); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
enum En_t InRange2 = (enum En_t)(-1); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
enum En_t InRange3 = (enum En_t)(0); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
enum En_t PosVal1 = (enum En_t)(1); // OK.
enum En_t PosVal2 = (enum En_t)(2); // OK.
enum En_t InRange4 = (enum En_t)(3); // expected-warning {{not in the valid range}}
enum En_t InRange4 = (enum En_t)(3); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
enum En_t PosVal3 = (enum En_t)(4); // OK.
enum En_t Above = (enum En_t)(5); // expected-warning {{not in the valid range}}
enum En_t Above = (enum En_t)(5); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
}

enum En_t unused;
Expand All @@ -32,3 +40,27 @@ void unusedExpr(void) {
// generate a warning having nothing to do with this checker.
unused; // expected-warning {{expression result unused}}
}

// Test typedef-ed anonymous enums
typedef enum { // expected-note {{enum declared here}}
TD_0 = 0,
} TD_t;

void testTypeDefEnum(void) {
(void)(TD_t)(-1); // expected-warning {{not in the valid range of values for the enum}}
// expected-note@-1 {{not in the valid range of values for the enum}}
}

// Test expression tracking
void set(int* p, int v) {
*p = v; // expected-note {{The value -1 is assigned to 'i'}}
}


void testTrackExpression(int i) {
set(&i, -1); // expected-note {{Passing the value -1 via 2nd parameter 'v'}}
// expected-note@-1 {{Calling 'set'}}
// expected-note@-2 {{Returning from 'set'}}
(void)(enum En_t)(i); // expected-warning {{not in the valid range of values for 'En_t'}}
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
}

0 comments on commit 8db38bd

Please sign in to comment.