Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2999,6 +2999,10 @@ def warn_objc_string_literal_comparison : Warning< "direct comparison of a string literal has undefined behavior">, InGroup; +def warn_concatenated_literal_array_init : Warning< + "concatenated literal in a string array initialization - " + "possibly missing a comma">, + InGroup>; def warn_concatenated_nsarray_literal : Warning< "concatenated NSString literal for an NSArray expression - " "possibly missing a comma">, @@ -6139,6 +6143,9 @@ def note_evaluate_comparison_first :Note< "place parentheses around comparison expression to evaluate it first">; +def note_concatenated_string_literal_silence :Note< + "place parentheses around string literal to silence warning">; + def warn_addition_in_bitshift : Warning< "operator '%0' has lower precedence than '%1'; " "'%1' will be evaluated first">, InGroup; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -6907,6 +6907,19 @@ << DIE->getSourceRange(); Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed) << InitArgList[I]->getSourceRange(); + } else if (const auto *SL = dyn_cast(InitArgList[I])) { + unsigned numConcat = SL->getNumConcatenated(); + // Diagnose missing comma in string array initialization. + if (numConcat > 1 && !SL->getBeginLoc().isMacroID()) { + SmallVector Hints; + for (unsigned i = 0; i < numConcat - 1; ++i) + Hints.push_back(FixItHint::CreateInsertion( + PP.getLocForEndOfToken(SL->getStrTokenLoc(i)), ", ")); + + Diag(SL->getStrTokenLoc(1), diag::warn_concatenated_literal_array_init) + << Hints; + Diag(SL->getBeginLoc(), diag::note_concatenated_string_literal_silence); + } } } Index: clang/test/Sema/string-concat.c =================================================================== --- /dev/null +++ clang/test/Sema/string-concat.c @@ -2,40 +2,84 @@ // RUN: %clang_cc1 -x c -fsyntax-only -verify %s // RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s -const char *test1[] = { +const char *missing_comma[] = { "basic_filebuf", "basic_ios", "future", "optional", - "packaged_task" // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} - "promise", - "shared_future", - "shared_lock", - "shared_ptr", - "thread", - "unique_ptr", - "unique_lock", - "weak_ptr", + "packaged_task" // expected-note{{place parentheses around string literal to silence warning}} + "promise", // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} + "promise", + "shared_future" }; -const char *test2[] = {"basic_filebuf", - "basic_ios" // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} - "future" +const char *missing_two_commas[] = {"basic_filebuf", + "basic_ios" // expected-note{{place parentheses around string literal to silence warning}} + "future" // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} "optional", "packaged_task"}; -const char *test3[] = {"basic_filebuf", "basic_ios", - "future" "optional", // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} - "packaged_task", "promise"}; +const char *missing_comma_same_line[] = {"basic_filebuf", "basic_ios", + "future" "optional", // expected-note{{place parentheses around string literal to silence warning}} + "packaged_task", "promise"}; // expected-warning@-1{{concatenated literal in a string array initialization - possibly missing a comma}} -const char *test4[] = {"basic_filebuf", "basic_ios" // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} - "future", "optional", +const char *missing_comma_different_lines[] = {"basic_filebuf", "basic_ios" // expected-note{{place parentheses around string literal to silence warning}} + "future", "optional", // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} "packaged_task", "promise"}; +const char *missing_comma_same_line_all_literals[] = {"basic_filebuf", "future" "optional", "packaged_task"}; // expected-note{{place parentheses around string literal to silence warning}} + // expected-warning@-1{{concatenated literal in a string array initialization - possibly missing a comma}} + +char missing_comma_inner[][4] = { + "a", + "b" // expected-note{{place parentheses around string literal to silence warning}} + "c" // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} +}; + + +#define ONE(x) x +#define TWO "foo" +const char *macro_test[] = { ONE("foo") "bar", + TWO "bar", + "foo" TWO // expected-note{{place parentheses around string literal to silence warning}} + }; // expected-warning@-1{{concatenated literal in a string array initialization - possibly missing a comma}} + +// Do not warn for macros. + #define BASIC_IOS "basic_ios" #define FUTURE "future" -const char *test5[] = {"basic_filebuf", BASIC_IOS // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} +const char *macro_test2[] = {"basic_filebuf", BASIC_IOS FUTURE, "optional", "packaged_task", "promise"}; -const char *test6[] = {"basic_filebuf", "future" "optional", "packaged_task"}; // expected-warning{{concatenated literal in a string array initialization - possibly missing a comma}} +#define FOO(xx) xx "_normal", \ + xx "_movable", + +const char *macro_test3[] = {"basic_filebuf", + "basic_ios", + FOO("future") + "optional", + "packaged_task"}; + +#define BAR(name) #name "_normal" + +const char *macro_test4[] = {"basic_filebuf", + "basic_ios", + BAR(future), + "optional", + "packaged_task"}; + +#define SUPPRESS(x) x +const char *macro_test5[] = { SUPPRESS("foo" "bar"), "baz" }; + +// Warning can be supressed also by extra parentheses. + +const char *extra_parens_to_suppress_warning[] = { + "basic_filebuf", + "basic_ios", + "future", + "optional", + ("packaged_task" + "promise"), + "shared_future" +}; \ No newline at end of file