Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ Improvements to Clang's diagnostics
an override of a virtual method.
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
parenthesis when diagnosing malformed fold expressions. (#GH151787)
- ``-Wstring-concatenation`` now diagnoses every missing comma in an initializer list,
rather than stopping after the first. (#GH153745)

- Fixed an issue where emitted format-signedness diagnostics were not associated with an appropriate
diagnostic id. Besides being incorrect from an API standpoint, this was user visible, e.g.:
Expand Down
45 changes: 26 additions & 19 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14708,7 +14708,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
isa<InitListExpr>(var->getInit())) {
const auto *ILE = cast<InitListExpr>(var->getInit());
unsigned NumInits = ILE->getNumInits();
if (NumInits > 2)
if (NumInits > 2) {
auto concatenatedPartsAt = [&](unsigned Index) -> unsigned {
const Expr *E = ILE->getInit(Index);
if (E) {
if (const auto *S = dyn_cast<StringLiteral>(E->IgnoreImpCasts()))
return S->getNumConcatenated();
}
return 0;
};

for (unsigned I = 0; I < NumInits; ++I) {
const auto *Init = ILE->getInit(I);
if (!Init)
Expand All @@ -14721,35 +14730,33 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// Diagnose missing comma in string array initialization.
// Do not warn when all the elements in the initializer are concatenated
// together. Do not warn for macros too.
if (NumConcat == 2 && !SL->getBeginLoc().isMacroID()) {
bool OnlyOneMissingComma = true;
for (unsigned J = I + 1; J < NumInits; ++J) {
const auto *Init = ILE->getInit(J);
if (!Init)
break;
const auto *SLJ = dyn_cast<StringLiteral>(Init->IgnoreImpCasts());
if (!SLJ || SLJ->getNumConcatenated() > 1) {
OnlyOneMissingComma = false;
break;
}
}
if (NumConcat == 2) {
if (SL->getBeginLoc().isMacroID())
continue;

unsigned L = I > 0 ? concatenatedPartsAt(I - 1) : 0;
unsigned R = I + 1 < NumInits ? concatenatedPartsAt(I + 1) : 0;

// Skip neighbors with multi-part concatenations.
if (L > 1 || R > 1)
continue;

if (OnlyOneMissingComma) {
// Diagnose when at least one neighbor is a single literal.
if (L || R) {
SmallVector<FixItHint, 1> Hints;
for (unsigned i = 0; i < NumConcat - 1; ++i)
Hints.push_back(FixItHint::CreateInsertion(
PP.getLocForEndOfToken(SL->getStrTokenLoc(i)), ","));
// Insert a comma between the two tokens of this element.
Hints.push_back(FixItHint::CreateInsertion(
PP.getLocForEndOfToken(SL->getStrTokenLoc(0)), ", "));

Diag(SL->getStrTokenLoc(1),
diag::warn_concatenated_literal_array_init)
<< Hints;
Diag(SL->getBeginLoc(),
diag::note_concatenated_string_literal_silence);
}
// In any case, stop now.
break;
}
}
}
}


Expand Down
14 changes: 14 additions & 0 deletions clang/test/Sema/string-concat.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,17 @@ const char *extra_parens_to_suppress_warning[] = {
"promise"),
"shared_future"
};

const char *multiple_missing_commas[] = {
"1",
"2" // expected-note {{place parentheses around the string literal to silence warning}}
"3", // expected-warning {{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
"4",
"5",
"6" // expected-note {{place parentheses around the string literal to silence warning}}
"7", // expected-warning {{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
"8",
"9",
"10",
"11",
};