forked from OSchip/llvm-project
[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:
parent
f575f12c64
commit
ba4017670e
|
@ -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<
|
||||||
|
|
|
@ -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)) &&
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue