Skip to content

Commit d9cd867

Browse files
committed
[Clang] improve -Wstring-concatenation to warn on every missing comma in initializer lists
1 parent a66d8f6 commit d9cd867

File tree

3 files changed

+42
-19
lines changed

3 files changed

+42
-19
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ Improvements to Clang's diagnostics
162162
an override of a virtual method.
163163
- Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right
164164
parenthesis when diagnosing malformed fold expressions. (#GH151787)
165+
- ``-Wstring-concatenation`` now diagnoses every missing comma in an initializer list,
166+
rather than stopping after the first. (#GH153745)
165167

166168
- Fixed an issue where emitted format-signedness diagnostics were not associated with an appropriate
167169
diagnostic id. Besides being incorrect from an API standpoint, this was user visible, e.g.:

clang/lib/Sema/SemaDecl.cpp

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14708,7 +14708,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1470814708
isa<InitListExpr>(var->getInit())) {
1470914709
const auto *ILE = cast<InitListExpr>(var->getInit());
1471014710
unsigned NumInits = ILE->getNumInits();
14711-
if (NumInits > 2)
14711+
if (NumInits > 2) {
14712+
auto concatenatedPartsAt = [&](unsigned Index) -> unsigned {
14713+
const Expr *E = ILE->getInit(Index);
14714+
if (E) {
14715+
if (const auto *S = dyn_cast<StringLiteral>(E->IgnoreImpCasts()))
14716+
return S->getNumConcatenated();
14717+
}
14718+
return 0;
14719+
};
14720+
1471214721
for (unsigned I = 0; I < NumInits; ++I) {
1471314722
const auto *Init = ILE->getInit(I);
1471414723
if (!Init)
@@ -14721,35 +14730,33 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1472114730
// Diagnose missing comma in string array initialization.
1472214731
// Do not warn when all the elements in the initializer are concatenated
1472314732
// together. Do not warn for macros too.
14724-
if (NumConcat == 2 && !SL->getBeginLoc().isMacroID()) {
14725-
bool OnlyOneMissingComma = true;
14726-
for (unsigned J = I + 1; J < NumInits; ++J) {
14727-
const auto *Init = ILE->getInit(J);
14728-
if (!Init)
14729-
break;
14730-
const auto *SLJ = dyn_cast<StringLiteral>(Init->IgnoreImpCasts());
14731-
if (!SLJ || SLJ->getNumConcatenated() > 1) {
14732-
OnlyOneMissingComma = false;
14733-
break;
14734-
}
14735-
}
14733+
if (NumConcat == 2) {
14734+
if (SL->getBeginLoc().isMacroID())
14735+
continue;
14736+
14737+
unsigned L = I > 0 ? concatenatedPartsAt(I - 1) : 0;
14738+
unsigned R = I + 1 < NumInits ? concatenatedPartsAt(I + 1) : 0;
14739+
14740+
// Skip neighbors with multi-part concatenations.
14741+
if (L > 1 || R > 1)
14742+
continue;
1473614743

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

1474314751
Diag(SL->getStrTokenLoc(1),
1474414752
diag::warn_concatenated_literal_array_init)
1474514753
<< Hints;
1474614754
Diag(SL->getBeginLoc(),
1474714755
diag::note_concatenated_string_literal_silence);
1474814756
}
14749-
// In any case, stop now.
14750-
break;
1475114757
}
1475214758
}
14759+
}
1475314760
}
1475414761

1475514762

clang/test/Sema/string-concat.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,17 @@ const char *extra_parens_to_suppress_warning[] = {
168168
"promise"),
169169
"shared_future"
170170
};
171+
172+
const char *multiple_missing_commas[] = {
173+
"1",
174+
"2" // expected-note {{place parentheses around the string literal to silence warning}}
175+
"3", // expected-warning {{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
176+
"4",
177+
"5",
178+
"6" // expected-note {{place parentheses around the string literal to silence warning}}
179+
"7", // expected-warning {{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
180+
"8",
181+
"9",
182+
"10",
183+
"11",
184+
};

0 commit comments

Comments
 (0)