Skip to content

Commit 8db38bd

Browse files
author
Endre Fülöp
authored
[analyzer] Extend EnumCastOutOfRange diagnostics (#68191)
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.
1 parent 6ed2d30 commit 8db38bd

File tree

3 files changed

+123
-69
lines changed

3 files changed

+123
-69
lines changed

clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp

+29-11
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ class ConstraintBasedEQEvaluator {
5959
// value can be matching.
6060
class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
6161
mutable std::unique_ptr<BugType> EnumValueCastOutOfRange;
62-
void reportWarning(CheckerContext &C) const;
62+
void reportWarning(CheckerContext &C, const CastExpr *CE,
63+
const EnumDecl *E) const;
6364

6465
public:
6566
void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
@@ -72,21 +73,38 @@ EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
7273
EnumValueVector DeclValues(
7374
std::distance(ED->enumerator_begin(), ED->enumerator_end()));
7475
llvm::transform(ED->enumerators(), DeclValues.begin(),
75-
[](const EnumConstantDecl *D) { return D->getInitVal(); });
76+
[](const EnumConstantDecl *D) { return D->getInitVal(); });
7677
return DeclValues;
7778
}
7879
} // namespace
7980

80-
void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
81+
void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C,
82+
const CastExpr *CE,
83+
const EnumDecl *E) const {
84+
assert(E && "valid EnumDecl* is expected");
8185
if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
8286
if (!EnumValueCastOutOfRange)
8387
EnumValueCastOutOfRange.reset(
8488
new BugType(this, "Enum cast out of range"));
85-
constexpr llvm::StringLiteral Msg =
86-
"The value provided to the cast expression is not in the valid range"
87-
" of values for the enum";
88-
C.emitReport(std::make_unique<PathSensitiveBugReport>(
89-
*EnumValueCastOutOfRange, Msg, N));
89+
90+
llvm::SmallString<128> Msg{"The value provided to the cast expression is "
91+
"not in the valid range of values for "};
92+
StringRef EnumName{E->getName()};
93+
if (EnumName.empty()) {
94+
Msg += "the enum";
95+
} else {
96+
Msg += '\'';
97+
Msg += EnumName;
98+
Msg += '\'';
99+
}
100+
101+
auto BR = std::make_unique<PathSensitiveBugReport>(*EnumValueCastOutOfRange,
102+
Msg, N);
103+
bugreporter::trackExpressionValue(N, CE->getSubExpr(), *BR);
104+
BR->addNote("enum declared here",
105+
PathDiagnosticLocation::create(E, C.getSourceManager()),
106+
{E->getSourceRange()});
107+
C.emitReport(std::move(BR));
90108
}
91109
}
92110

@@ -138,13 +156,13 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
138156
return;
139157

140158
// Check if any of the enum values possibly match.
141-
bool PossibleValueMatch = llvm::any_of(
142-
DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
159+
bool PossibleValueMatch =
160+
llvm::any_of(DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
143161

144162
// If there is no value that can possibly match any of the enum values, then
145163
// warn.
146164
if (!PossibleValueMatch)
147-
reportWarning(C);
165+
reportWarning(C, CE, ED);
148166
}
149167

150168
void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
+38-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// RUN: %clang_analyze_cc1 \
22
// RUN: -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
3+
// RUN: -analyzer-output text \
34
// RUN: -verify %s
45

6+
// expected-note@+1 + {{enum declared here}}
57
enum En_t {
68
En_0 = -4,
79
En_1,
@@ -11,17 +13,23 @@ enum En_t {
1113
};
1214

1315
void unscopedUnspecifiedCStyle(void) {
14-
enum En_t Below = (enum En_t)(-5); // expected-warning {{not in the valid range}}
16+
enum En_t Below = (enum En_t)(-5); // expected-warning {{not in the valid range of values for 'En_t'}}
17+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
1518
enum En_t NegVal1 = (enum En_t)(-4); // OK.
1619
enum En_t NegVal2 = (enum En_t)(-3); // OK.
17-
enum En_t InRange1 = (enum En_t)(-2); // expected-warning {{not in the valid range}}
18-
enum En_t InRange2 = (enum En_t)(-1); // expected-warning {{not in the valid range}}
19-
enum En_t InRange3 = (enum En_t)(0); // expected-warning {{not in the valid range}}
20+
enum En_t InRange1 = (enum En_t)(-2); // expected-warning {{not in the valid range of values for 'En_t'}}
21+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
22+
enum En_t InRange2 = (enum En_t)(-1); // expected-warning {{not in the valid range of values for 'En_t'}}
23+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
24+
enum En_t InRange3 = (enum En_t)(0); // expected-warning {{not in the valid range of values for 'En_t'}}
25+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
2026
enum En_t PosVal1 = (enum En_t)(1); // OK.
2127
enum En_t PosVal2 = (enum En_t)(2); // OK.
22-
enum En_t InRange4 = (enum En_t)(3); // expected-warning {{not in the valid range}}
28+
enum En_t InRange4 = (enum En_t)(3); // expected-warning {{not in the valid range of values for 'En_t'}}
29+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
2330
enum En_t PosVal3 = (enum En_t)(4); // OK.
24-
enum En_t Above = (enum En_t)(5); // expected-warning {{not in the valid range}}
31+
enum En_t Above = (enum En_t)(5); // expected-warning {{not in the valid range of values for 'En_t'}}
32+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
2533
}
2634

2735
enum En_t unused;
@@ -32,3 +40,27 @@ void unusedExpr(void) {
3240
// generate a warning having nothing to do with this checker.
3341
unused; // expected-warning {{expression result unused}}
3442
}
43+
44+
// Test typedef-ed anonymous enums
45+
typedef enum { // expected-note {{enum declared here}}
46+
TD_0 = 0,
47+
} TD_t;
48+
49+
void testTypeDefEnum(void) {
50+
(void)(TD_t)(-1); // expected-warning {{not in the valid range of values for the enum}}
51+
// expected-note@-1 {{not in the valid range of values for the enum}}
52+
}
53+
54+
// Test expression tracking
55+
void set(int* p, int v) {
56+
*p = v; // expected-note {{The value -1 is assigned to 'i'}}
57+
}
58+
59+
60+
void testTrackExpression(int i) {
61+
set(&i, -1); // expected-note {{Passing the value -1 via 2nd parameter 'v'}}
62+
// expected-note@-1 {{Calling 'set'}}
63+
// expected-note@-2 {{Returning from 'set'}}
64+
(void)(enum En_t)(i); // expected-warning {{not in the valid range of values for 'En_t'}}
65+
// expected-note@-1 {{not in the valid range of values for 'En_t'}}
66+
}

0 commit comments

Comments
 (0)