Skip to content

Commit 60bd88d

Browse files
committed
[analyzer] Extend IdenticalExprChecker to check ternary operator results.
Warn if both result expressions of a ternary operator (? :) are the same. Because only one of them will be executed, this warning will fire even if the expressions have side effects. Patch by Anders Rönnholm and Per Viberg! llvm-svn: 196937
1 parent 8d96c80 commit 60bd88d

File tree

4 files changed

+269
-8
lines changed

4 files changed

+269
-8
lines changed

clang/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include <vector>
2828

2929
namespace clang {
30-
30+
class ConditionalOperator;
3131
class AnalysisDeclContext;
3232
class BinaryOperator;
3333
class CompoundStmt;
@@ -211,6 +211,9 @@ class PathDiagnosticLocation {
211211
/// Assumes the statement has a valid location.
212212
static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
213213
const SourceManager &SM);
214+
static PathDiagnosticLocation createConditionalColonLoc(
215+
const ConditionalOperator *CO,
216+
const SourceManager &SM);
214217

215218
/// For member expressions, return the location of the '.' or '->'.
216219
/// Assumes the statement has a valid location.

clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
/// \brief This defines IdenticalExprChecker, a check that warns about
1212
/// unintended use of identical expressions.
1313
///
14-
/// It checks for use of identical expressions with comparison operators.
14+
/// It checks for use of identical expressions with comparison operators and
15+
/// inside conditional expressions.
1516
///
1617
//===----------------------------------------------------------------------===//
1718

@@ -26,7 +27,7 @@ using namespace clang;
2627
using namespace ento;
2728

2829
static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
29-
const Expr *Expr2);
30+
const Expr *Expr2, bool IgnoreSideEffects = false);
3031
//===----------------------------------------------------------------------===//
3132
// FindIdenticalExprVisitor - Identify nodes using identical expressions.
3233
//===----------------------------------------------------------------------===//
@@ -38,8 +39,9 @@ class FindIdenticalExprVisitor
3839
explicit FindIdenticalExprVisitor(BugReporter &B, AnalysisDeclContext *A)
3940
: BR(B), AC(A) {}
4041
// FindIdenticalExprVisitor only visits nodes
41-
// that are binary operators.
42+
// that are binary operators or conditional operators.
4243
bool VisitBinaryOperator(const BinaryOperator *B);
44+
bool VisitConditionalOperator(const ConditionalOperator *C);
4345

4446
private:
4547
BugReporter &BR;
@@ -118,6 +120,34 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
118120
// True is always returned to traverse ALL nodes.
119121
return true;
120122
}
123+
124+
bool FindIdenticalExprVisitor::VisitConditionalOperator(
125+
const ConditionalOperator *C) {
126+
127+
// Check if expressions in conditional expression are identical
128+
// from a symbolic point of view.
129+
130+
if (isIdenticalExpr(AC->getASTContext(), C->getTrueExpr(),
131+
C->getFalseExpr(), true)) {
132+
PathDiagnosticLocation ELoc =
133+
PathDiagnosticLocation::createConditionalColonLoc(
134+
C, BR.getSourceManager());
135+
136+
SourceRange Sr[2];
137+
Sr[0] = C->getTrueExpr()->getSourceRange();
138+
Sr[1] = C->getFalseExpr()->getSourceRange();
139+
BR.EmitBasicReport(
140+
AC->getDecl(), "Identical expressions in conditional expression",
141+
categories::LogicError,
142+
"identical expressions on both sides of ':' in conditional expression",
143+
ELoc, Sr);
144+
}
145+
// We want to visit ALL nodes (expressions in conditional
146+
// expressions too) that contains conditional operators,
147+
// thus always return true to traverse ALL nodes.
148+
return true;
149+
}
150+
121151
/// \brief Determines whether two expression trees are identical regarding
122152
/// operators and symbols.
123153
///
@@ -127,14 +157,14 @@ bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
127157
/// t*(u + t) and t*u + t*t are not considered identical.
128158
///
129159
static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
130-
const Expr *Expr2) {
160+
const Expr *Expr2, bool IgnoreSideEffects) {
131161
// If Expr1 & Expr2 are of different class then they are not
132162
// identical expression.
133163
if (Expr1->getStmtClass() != Expr2->getStmtClass())
134164
return false;
135165
// If Expr1 has side effects then don't warn even if expressions
136166
// are identical.
137-
if (Expr1->HasSideEffects(Ctx))
167+
if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
138168
return false;
139169
// Is expression is based on macro then don't warn even if
140170
// the expressions are identical.
@@ -146,7 +176,8 @@ static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
146176
while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
147177
const Expr *Child1 = dyn_cast<Expr>(*I1);
148178
const Expr *Child2 = dyn_cast<Expr>(*I2);
149-
if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2))
179+
if (!Child1 || !Child2 || !isIdenticalExpr(Ctx, Child1, Child2,
180+
IgnoreSideEffects))
150181
return false;
151182
++I1;
152183
++I2;
@@ -161,6 +192,7 @@ static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
161192
switch (Expr1->getStmtClass()) {
162193
default:
163194
return false;
195+
case Stmt::CallExprClass:
164196
case Stmt::ArraySubscriptExprClass:
165197
case Stmt::CStyleCastExprClass:
166198
case Stmt::ImplicitCastExprClass:
@@ -201,7 +233,7 @@ static bool isIdenticalExpr(const ASTContext &Ctx, const Expr *Expr1,
201233
const UnaryOperator *UnaryOp2 = dyn_cast<UnaryOperator>(Expr2);
202234
if (UnaryOp1->getOpcode() != UnaryOp2->getOpcode())
203235
return false;
204-
return !UnaryOp1->isIncrementDecrementOp();
236+
return IgnoreSideEffects || !UnaryOp1->isIncrementDecrementOp();
205237
}
206238
}
207239
}

clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,14 @@ PathDiagnosticLocation
609609
return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
610610
}
611611

612+
PathDiagnosticLocation
613+
PathDiagnosticLocation::createConditionalColonLoc(
614+
const ConditionalOperator *CO,
615+
const SourceManager &SM) {
616+
return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);
617+
}
618+
619+
612620
PathDiagnosticLocation
613621
PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
614622
const SourceManager &SM) {

clang/test/Analysis/identical-expressions.cpp

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
/* Only one expected warning per function allowed at the very end. */
44

5+
int func(void)
6+
{
7+
return 0;
8+
}
9+
10+
int func2(void)
11+
{
12+
return 0;
13+
}
14+
15+
int funcParam(int a)
16+
{
17+
return 0;
18+
}
19+
520
/* '!=' operator*/
621

722
/* '!=' with float */
@@ -295,6 +310,38 @@ int checkNotEqualNestedBinaryOpIntPointerCompare2(void) {
295310
}
296311
/* end '!=' int* */
297312

313+
/* '!=' with function*/
314+
315+
int checkNotEqualSameFunction() {
316+
unsigned a = 0;
317+
unsigned b = 1;
318+
int res = (a+func() != a+func()); // no warning
319+
return (0);
320+
}
321+
322+
int checkNotEqualDifferentFunction() {
323+
unsigned a = 0;
324+
unsigned b = 1;
325+
int res = (a+func() != a+func2()); // no warning
326+
return (0);
327+
}
328+
329+
int checkNotEqualSameFunctionSameParam() {
330+
unsigned a = 0;
331+
unsigned b = 1;
332+
int res = (a+funcParam(a) != a+funcParam(a)); // no warning
333+
return (0);
334+
}
335+
336+
int checkNotEqualSameFunctionDifferentParam() {
337+
unsigned a = 0;
338+
unsigned b = 1;
339+
int res = (a+funcParam(a) != a+funcParam(b)); // no warning
340+
return (0);
341+
}
342+
343+
/* end '!=' with function*/
344+
298345
/* end '!=' */
299346

300347

@@ -526,6 +573,37 @@ int checkEqualNestedBinaryOpIntCompare3(void) {
526573
return (0);
527574
}
528575

576+
/* '==' with function*/
577+
578+
int checkEqualSameFunction() {
579+
unsigned a = 0;
580+
unsigned b = 1;
581+
int res = (a+func() == a+func()); // no warning
582+
return (0);
583+
}
584+
585+
int checkEqualDifferentFunction() {
586+
unsigned a = 0;
587+
unsigned b = 1;
588+
int res = (a+func() == a+func2()); // no warning
589+
return (0);
590+
}
591+
592+
int checkEqualSameFunctionSameParam() {
593+
unsigned a = 0;
594+
unsigned b = 1;
595+
int res = (a+funcParam(a) == a+funcParam(a)); // no warning
596+
return (0);
597+
}
598+
599+
int checkEqualSameFunctionDifferentParam() {
600+
unsigned a = 0;
601+
unsigned b = 1;
602+
int res = (a+funcParam(a) == a+funcParam(b)); // no warning
603+
return (0);
604+
}
605+
606+
/* end '==' with function*/
529607

530608
/* end EQ int */
531609

@@ -940,3 +1018,143 @@ int checkGreaterThanNestedBinaryOpIntCompare3(void) {
9401018
/* end GT with int */
9411019

9421020
/* end GT */
1021+
1022+
1023+
/* Checking use of identical expressions in conditional operator*/
1024+
1025+
unsigned test_unsigned(unsigned a) {
1026+
unsigned b = 1;
1027+
a = a > 5 ? b : b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1028+
return a;
1029+
}
1030+
1031+
void test_signed() {
1032+
int a = 0;
1033+
a = a > 5 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1034+
}
1035+
1036+
void test_bool(bool a) {
1037+
a = a > 0 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1038+
}
1039+
1040+
void test_float() {
1041+
float a = 0;
1042+
float b = 0;
1043+
a = a > 5 ? a : a; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1044+
}
1045+
1046+
void test_unsigned_expr() {
1047+
unsigned a = 0;
1048+
unsigned b = 0;
1049+
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1050+
}
1051+
1052+
void test_signed_expr() {
1053+
int a = 0;
1054+
int b = 1;
1055+
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1056+
}
1057+
1058+
void test_bool_expr(bool a) {
1059+
bool b = 0;
1060+
a = a > 0 ? a&&b : a&&b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1061+
}
1062+
1063+
void test_unsigned_expr_negative() {
1064+
unsigned a = 0;
1065+
unsigned b = 0;
1066+
a = a > 5 ? a+b : b+a; // no warning
1067+
}
1068+
1069+
void test_signed_expr_negative() {
1070+
int a = 0;
1071+
int b = 1;
1072+
a = a > 5 ? b+a : a+b; // no warning
1073+
}
1074+
1075+
void test_bool_expr_negative(bool a) {
1076+
bool b = 0;
1077+
a = a > 0 ? a&&b : b&&a; // no warning
1078+
}
1079+
1080+
void test_float_expr_positive() {
1081+
float a = 0;
1082+
float b = 0;
1083+
a = a > 5 ? a+b : a+b; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1084+
}
1085+
1086+
void test_expr_positive_func() {
1087+
unsigned a = 0;
1088+
unsigned b = 1;
1089+
a = a > 5 ? a+func() : a+func(); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1090+
}
1091+
1092+
void test_expr_negative_func() {
1093+
unsigned a = 0;
1094+
unsigned b = 1;
1095+
a = a > 5 ? a+func() : a+func2(); // no warning
1096+
}
1097+
1098+
void test_expr_positive_funcParam() {
1099+
unsigned a = 0;
1100+
unsigned b = 1;
1101+
a = a > 5 ? a+funcParam(b) : a+funcParam(b); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1102+
}
1103+
1104+
void test_expr_negative_funcParam() {
1105+
unsigned a = 0;
1106+
unsigned b = 1;
1107+
a = a > 5 ? a+funcParam(a) : a+funcParam(b); // no warning
1108+
}
1109+
1110+
void test_expr_positive_inc() {
1111+
unsigned a = 0;
1112+
unsigned b = 1;
1113+
a = a > 5 ? a++ : a++; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1114+
}
1115+
1116+
void test_expr_negative_inc() {
1117+
unsigned a = 0;
1118+
unsigned b = 1;
1119+
a = a > 5 ? a++ : b++; // no warning
1120+
}
1121+
1122+
void test_expr_positive_assign() {
1123+
unsigned a = 0;
1124+
unsigned b = 1;
1125+
a = a > 5 ? a=1 : a=1; // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1126+
}
1127+
1128+
void test_expr_negative_assign() {
1129+
unsigned a = 0;
1130+
unsigned b = 1;
1131+
a = a > 5 ? a=1 : a=2; // no warning
1132+
}
1133+
1134+
void test_signed_nested_expr() {
1135+
int a = 0;
1136+
int b = 1;
1137+
int c = 3;
1138+
a = a > 5 ? a+b+(c+a)*(a + b*(c+a)) : a+b+(c+a)*(a + b*(c+a)); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1139+
}
1140+
1141+
void test_signed_nested_expr_negative() {
1142+
int a = 0;
1143+
int b = 1;
1144+
int c = 3;
1145+
a = a > 5 ? a+b+(c+a)*(a + b*(c+a)) : a+b+(c+a)*(a + b*(a+c)); // no warning
1146+
}
1147+
1148+
void test_signed_nested_cond_expr_negative() {
1149+
int a = 0;
1150+
int b = 1;
1151+
int c = 3;
1152+
a = a > 5 ? (b > 5 ? 1 : 4) : (b > 5 ? 2 : 4); // no warning
1153+
}
1154+
1155+
void test_signed_nested_cond_expr() {
1156+
int a = 0;
1157+
int b = 1;
1158+
int c = 3;
1159+
a = a > 5 ? (b > 5 ? 1 : 4) : (b > 5 ? 4 : 4); // expected-warning {{identical expressions on both sides of ':' in conditional expression}}
1160+
}

0 commit comments

Comments
 (0)