Skip to content

Commit ba40176

Browse files
[Diagnostics] Warn for comparison with string literals expanded from macro (PR44064)
Summary: As noted in PR, we have a poor test coverage for this warning. I think macro support was just overlooked. GCC warns in these cases. Clang missed a real bug in the code I am working with, GCC caught it. Reviewers: aaron.ballman Reviewed By: aaron.ballman Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D70624
1 parent f575f12 commit ba40176

File tree

4 files changed

+76
-42
lines changed

4 files changed

+76
-42
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8428,7 +8428,7 @@ def warn_tautological_overlap_comparison : Warning<
84288428

84298429
def warn_stringcompare : Warning<
84308430
"result of comparison against %select{a string literal|@encode}0 is "
8431-
"unspecified (use strncmp instead)">,
8431+
"unspecified (use an explicit string comparison function instead)">,
84328432
InGroup<StringCompare>;
84338433

84348434
def warn_identity_field_assign : Warning<

clang/lib/Sema/SemaExpr.cpp

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10338,7 +10338,6 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
1033810338
QualType RHSType = RHS->getType();
1033910339
if (LHSType->hasFloatingRepresentation() ||
1034010340
(LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) ||
10341-
LHS->getBeginLoc().isMacroID() || RHS->getBeginLoc().isMacroID() ||
1034210341
S.inTemplateInstantiation())
1034310342
return;
1034410343

@@ -10366,45 +10365,51 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
1036610365
AlwaysEqual, // std::strong_ordering::equal from operator<=>
1036710366
};
1036810367

10369-
if (Expr::isSameComparisonOperand(LHS, RHS)) {
10370-
unsigned Result;
10371-
switch (Opc) {
10372-
case BO_EQ: case BO_LE: case BO_GE:
10373-
Result = AlwaysTrue;
10374-
break;
10375-
case BO_NE: case BO_LT: case BO_GT:
10376-
Result = AlwaysFalse;
10377-
break;
10378-
case BO_Cmp:
10379-
Result = AlwaysEqual;
10380-
break;
10381-
default:
10382-
Result = AlwaysConstant;
10383-
break;
10384-
}
10385-
S.DiagRuntimeBehavior(Loc, nullptr,
10386-
S.PDiag(diag::warn_comparison_always)
10387-
<< 0 /*self-comparison*/
10388-
<< Result);
10389-
} else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) {
10390-
// What is it always going to evaluate to?
10391-
unsigned Result;
10392-
switch(Opc) {
10393-
case BO_EQ: // e.g. array1 == array2
10394-
Result = AlwaysFalse;
10395-
break;
10396-
case BO_NE: // e.g. array1 != array2
10397-
Result = AlwaysTrue;
10398-
break;
10399-
default: // e.g. array1 <= array2
10400-
// The best we can say is 'a constant'
10401-
Result = AlwaysConstant;
10402-
break;
10368+
if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) {
10369+
if (Expr::isSameComparisonOperand(LHS, RHS)) {
10370+
unsigned Result;
10371+
switch (Opc) {
10372+
case BO_EQ:
10373+
case BO_LE:
10374+
case BO_GE:
10375+
Result = AlwaysTrue;
10376+
break;
10377+
case BO_NE:
10378+
case BO_LT:
10379+
case BO_GT:
10380+
Result = AlwaysFalse;
10381+
break;
10382+
case BO_Cmp:
10383+
Result = AlwaysEqual;
10384+
break;
10385+
default:
10386+
Result = AlwaysConstant;
10387+
break;
10388+
}
10389+
S.DiagRuntimeBehavior(Loc, nullptr,
10390+
S.PDiag(diag::warn_comparison_always)
10391+
<< 0 /*self-comparison*/
10392+
<< Result);
10393+
} else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) {
10394+
// What is it always going to evaluate to?
10395+
unsigned Result;
10396+
switch (Opc) {
10397+
case BO_EQ: // e.g. array1 == array2
10398+
Result = AlwaysFalse;
10399+
break;
10400+
case BO_NE: // e.g. array1 != array2
10401+
Result = AlwaysTrue;
10402+
break;
10403+
default: // e.g. array1 <= array2
10404+
// The best we can say is 'a constant'
10405+
Result = AlwaysConstant;
10406+
break;
10407+
}
10408+
S.DiagRuntimeBehavior(Loc, nullptr,
10409+
S.PDiag(diag::warn_comparison_always)
10410+
<< 1 /*array comparison*/
10411+
<< Result);
1040310412
}
10404-
S.DiagRuntimeBehavior(Loc, nullptr,
10405-
S.PDiag(diag::warn_comparison_always)
10406-
<< 1 /*array comparison*/
10407-
<< Result);
1040810413
}
1040910414

1041010415
if (isa<CastExpr>(LHSStripped))
@@ -10413,7 +10418,7 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
1041310418
RHSStripped = RHSStripped->IgnoreParenCasts();
1041410419

1041510420
// Warn about comparisons against a string constant (unless the other
10416-
// operand is null); the user probably wants strcmp.
10421+
// operand is null); the user probably wants string comparison function.
1041710422
Expr *LiteralString = nullptr;
1041810423
Expr *LiteralStringStripped = nullptr;
1041910424
if ((isa<StringLiteral>(LHSStripped) || isa<ObjCEncodeExpr>(LHSStripped)) &&

clang/test/Sema/exprs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ void test11(struct mystruct P, float F) {
119119

120120
// PR3753
121121
int test12(const char *X) {
122-
return X == "foo"; // expected-warning {{comparison against a string literal is unspecified (use strncmp instead)}}
122+
return X == "foo"; // expected-warning {{comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
123123
}
124124

125125
int test12b(const char *X) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -x c -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s
3+
4+
#define DELIM "/"
5+
#define DOT "."
6+
#define NULL (void *)0
7+
8+
void test(const char *d) {
9+
if ("/" != d) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
10+
return;
11+
if (d == "/") // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
12+
return;
13+
if ("/" != NULL)
14+
return;
15+
if (NULL == "/")
16+
return;
17+
if ("/" != DELIM) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
18+
return;
19+
if (DELIM == "/") // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
20+
return;
21+
if (DELIM != NULL)
22+
return;
23+
if (NULL == DELIM)
24+
return;
25+
if (DOT != DELIM) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
26+
return;
27+
if (DELIM == DOT) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
28+
return;
29+
}

0 commit comments

Comments
 (0)