[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
This commit is contained in:
Dávid Bolvanský 2019-11-24 16:30:45 +01:00
parent f575f12c64
commit ba4017670e
4 changed files with 76 additions and 42 deletions

View File

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

View File

@ -10338,7 +10338,6 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
QualType RHSType = RHS->getType(); QualType RHSType = RHS->getType();
if (LHSType->hasFloatingRepresentation() || if (LHSType->hasFloatingRepresentation() ||
(LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) || (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) ||
LHS->getBeginLoc().isMacroID() || RHS->getBeginLoc().isMacroID() ||
S.inTemplateInstantiation()) S.inTemplateInstantiation())
return; return;
@ -10366,45 +10365,51 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
AlwaysEqual, // std::strong_ordering::equal from operator<=> AlwaysEqual, // std::strong_ordering::equal from operator<=>
}; };
if (Expr::isSameComparisonOperand(LHS, RHS)) { if (!LHS->getBeginLoc().isMacroID() && !RHS->getBeginLoc().isMacroID()) {
unsigned Result; if (Expr::isSameComparisonOperand(LHS, RHS)) {
switch (Opc) { unsigned Result;
case BO_EQ: case BO_LE: case BO_GE: switch (Opc) {
Result = AlwaysTrue; case BO_EQ:
break; case BO_LE:
case BO_NE: case BO_LT: case BO_GT: case BO_GE:
Result = AlwaysFalse; Result = AlwaysTrue;
break; break;
case BO_Cmp: case BO_NE:
Result = AlwaysEqual; case BO_LT:
break; case BO_GT:
default: Result = AlwaysFalse;
Result = AlwaysConstant; break;
break; case BO_Cmp:
Result = AlwaysEqual;
break;
default:
Result = AlwaysConstant;
break;
}
S.DiagRuntimeBehavior(Loc, nullptr,
S.PDiag(diag::warn_comparison_always)
<< 0 /*self-comparison*/
<< Result);
} else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) {
// What is it always going to evaluate to?
unsigned Result;
switch (Opc) {
case BO_EQ: // e.g. array1 == array2
Result = AlwaysFalse;
break;
case BO_NE: // e.g. array1 != array2
Result = AlwaysTrue;
break;
default: // e.g. array1 <= array2
// The best we can say is 'a constant'
Result = AlwaysConstant;
break;
}
S.DiagRuntimeBehavior(Loc, nullptr,
S.PDiag(diag::warn_comparison_always)
<< 1 /*array comparison*/
<< Result);
} }
S.DiagRuntimeBehavior(Loc, nullptr,
S.PDiag(diag::warn_comparison_always)
<< 0 /*self-comparison*/
<< Result);
} else if (checkForArray(LHSStripped) && checkForArray(RHSStripped)) {
// What is it always going to evaluate to?
unsigned Result;
switch(Opc) {
case BO_EQ: // e.g. array1 == array2
Result = AlwaysFalse;
break;
case BO_NE: // e.g. array1 != array2
Result = AlwaysTrue;
break;
default: // e.g. array1 <= array2
// The best we can say is 'a constant'
Result = AlwaysConstant;
break;
}
S.DiagRuntimeBehavior(Loc, nullptr,
S.PDiag(diag::warn_comparison_always)
<< 1 /*array comparison*/
<< Result);
} }
if (isa<CastExpr>(LHSStripped)) if (isa<CastExpr>(LHSStripped))
@ -10413,7 +10418,7 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
RHSStripped = RHSStripped->IgnoreParenCasts(); RHSStripped = RHSStripped->IgnoreParenCasts();
// Warn about comparisons against a string constant (unless the other // Warn about comparisons against a string constant (unless the other
// operand is null); the user probably wants strcmp. // operand is null); the user probably wants string comparison function.
Expr *LiteralString = nullptr; Expr *LiteralString = nullptr;
Expr *LiteralStringStripped = nullptr; Expr *LiteralStringStripped = nullptr;
if ((isa<StringLiteral>(LHSStripped) || isa<ObjCEncodeExpr>(LHSStripped)) && if ((isa<StringLiteral>(LHSStripped) || isa<ObjCEncodeExpr>(LHSStripped)) &&

View File

@ -119,7 +119,7 @@ void test11(struct mystruct P, float F) {
// PR3753 // PR3753
int test12(const char *X) { int test12(const char *X) {
return X == "foo"; // expected-warning {{comparison against a string literal is unspecified (use strncmp instead)}} return X == "foo"; // expected-warning {{comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
} }
int test12b(const char *X) { int test12b(const char *X) {

View File

@ -0,0 +1,29 @@
// RUN: %clang_cc1 -x c -fsyntax-only -verify %s
// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s
#define DELIM "/"
#define DOT "."
#define NULL (void *)0
void test(const char *d) {
if ("/" != d) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
if (d == "/") // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
if ("/" != NULL)
return;
if (NULL == "/")
return;
if ("/" != DELIM) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
if (DELIM == "/") // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
if (DELIM != NULL)
return;
if (NULL == DELIM)
return;
if (DOT != DELIM) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
if (DELIM == DOT) // expected-warning {{result of comparison against a string literal is unspecified (use an explicit string comparison function instead)}}
return;
}