Add -Wstring-plus-char, which warns when adding char literals to C strings.

Specifically, this warns when a character literal is added (using '+') to a
variable with type 'char *' (or any other pointer to character type). Like
-Wstring-plus-int, there is a fix-it to change "foo + 'a'" to "&foo['a']"
iff the character literal is on the right side of the string.

Patch by Anders Rönnholm!

llvm-svn: 193418
This commit is contained in:
Jordan Rose 2013-10-25 16:52:00 +00:00
parent c0fdb3941c
commit 5565941eff
5 changed files with 109 additions and 5 deletions

View File

@ -269,6 +269,7 @@ def GNUStaticFloatInit : DiagGroup<"gnu-static-float-init">;
def StaticFloatInit : DiagGroup<"static-float-init", [GNUStaticFloatInit]>;
def GNUStatementExpression : DiagGroup<"gnu-statement-expression">;
def StringPlusInt : DiagGroup<"string-plus-int">;
def StringPlusChar : DiagGroup<"string-plus-char">;
def StrncatSize : DiagGroup<"strncat-size">;
def TautologicalOutOfRangeCompare : DiagGroup<"tautological-constant-out-of-range-compare">;
def TautologicalCompare : DiagGroup<"tautological-compare",

View File

@ -4292,7 +4292,10 @@ def warn_self_assignment : Warning<
def warn_string_plus_int : Warning<
"adding %0 to a string does not append to the string">,
InGroup<StringPlusInt>;
def note_string_plus_int_silence : Note<
def warn_string_plus_char : Warning<
"adding %0 to a string pointer does not append to the string">,
InGroup<StringPlusChar>;
def note_string_plus_scalar_silence : Note<
"use array indexing to silence this warning">;
def warn_sizeof_array_param : Warning<

View File

@ -6963,12 +6963,63 @@ static void diagnoseStringPlusInt(Sema &Self, SourceLocation OpLoc,
// Only print a fixit for "str" + int, not for int + "str".
if (IndexExpr == RHSExpr) {
SourceLocation EndLoc = Self.PP.getLocForEndOfToken(RHSExpr->getLocEnd());
Self.Diag(OpLoc, diag::note_string_plus_int_silence)
Self.Diag(OpLoc, diag::note_string_plus_scalar_silence)
<< FixItHint::CreateInsertion(LHSExpr->getLocStart(), "&")
<< FixItHint::CreateReplacement(SourceRange(OpLoc), "[")
<< FixItHint::CreateInsertion(EndLoc, "]");
} else
Self.Diag(OpLoc, diag::note_string_plus_int_silence);
Self.Diag(OpLoc, diag::note_string_plus_scalar_silence);
}
/// \brief Emit a warning when adding a char literal to a string.
static void diagnoseStringPlusChar(Sema &Self, SourceLocation OpLoc,
Expr *LHSExpr, Expr *RHSExpr) {
const DeclRefExpr *StringRefExpr =
dyn_cast<DeclRefExpr>(LHSExpr->IgnoreImpCasts());
const CharacterLiteral *CharExpr =
dyn_cast<CharacterLiteral>(RHSExpr->IgnoreImpCasts());
if (!StringRefExpr) {
StringRefExpr = dyn_cast<DeclRefExpr>(RHSExpr->IgnoreImpCasts());
CharExpr = dyn_cast<CharacterLiteral>(LHSExpr->IgnoreImpCasts());
}
if (!CharExpr || !StringRefExpr)
return;
const QualType StringType = StringRefExpr->getType();
// Return if not a PointerType.
if (!StringType->isAnyPointerType())
return;
// Return if not a CharacterType.
if (!StringType->getPointeeType()->isAnyCharacterType())
return;
ASTContext &Ctx = Self.getASTContext();
SourceRange DiagRange(LHSExpr->getLocStart(), RHSExpr->getLocEnd());
const QualType CharType = CharExpr->getType();
if (!CharType->isAnyCharacterType() &&
CharType->isIntegerType() &&
llvm::isUIntN(Ctx.getCharWidth(), CharExpr->getValue())) {
Self.Diag(OpLoc, diag::warn_string_plus_char)
<< DiagRange << Ctx.CharTy;
} else {
Self.Diag(OpLoc, diag::warn_string_plus_char)
<< DiagRange << CharExpr->getType();
}
// Only print a fixit for str + char, not for char + str.
if (isa<CharacterLiteral>(RHSExpr->IgnoreImpCasts())) {
SourceLocation EndLoc = Self.PP.getLocForEndOfToken(RHSExpr->getLocEnd());
Self.Diag(OpLoc, diag::note_string_plus_scalar_silence)
<< FixItHint::CreateInsertion(LHSExpr->getLocStart(), "&")
<< FixItHint::CreateReplacement(SourceRange(OpLoc), "[")
<< FixItHint::CreateInsertion(EndLoc, "]");
} else {
Self.Diag(OpLoc, diag::note_string_plus_scalar_silence);
}
}
/// \brief Emit error when two pointers are incompatible.
@ -6997,9 +7048,11 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
// Diagnose "string literal" '+' int.
if (Opc == BO_Add)
// Diagnose "string literal" '+' int and string '+' "char literal".
if (Opc == BO_Add) {
diagnoseStringPlusInt(*this, Loc, LHS.get(), RHS.get());
diagnoseStringPlusChar(*this, Loc, LHS.get(), RHS.get());
}
// handle the common case first (both operands are arithmetic).
if (!compType.isNull() && compType->isArithmeticType()) {

View File

@ -0,0 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
void f(const char *s) {
char *str = 0;
char *str2 = str + 'c'; // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
const char *constStr = s + 'c'; // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
str = 'c' + str;// expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
// no-warning
char c = 'c';
str = str + c;
str = c + str;
}

View File

@ -0,0 +1,32 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
class A {
public:
A(): str() { }
A(const char *p) { }
A(char *p) : str(p + 'a') { } // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
A& operator+(const char *p) { return *this; }
A& operator+(char ch) { return *this; }
char * str;
};
void f(const char *s) {
A a = s + 'a'; // // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
a = a + s + 'b'; // no-warning
char *str = 0;
char *str2 = str + 'c'; // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
const char *constStr = s + 'c'; // expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
str = 'c' + str;// expected-warning {{adding 'char' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
wchar_t *wstr;
wstr = wstr + L'c'; // expected-warning {{adding 'wchar_t' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
str2 = str + u'a'; // expected-warning {{adding 'char16_t' to a string pointer does not append to the string}} expected-note {{use array indexing to silence this warning}}
// no-warning
char c = 'c';
str = str + c;
str = c + str;
}