[clang] dc096a6 - [Diagnostics] Diagnose missing comma in string array initialization

Dávid Bolvanský via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 8 10:24:45 PDT 2020


Author: Dávid Bolvanský
Date: 2020-08-08T19:24:30+02:00
New Revision: dc096a66cb519532121fb0fbedb13265bd4b29ec

URL: https://github.com/llvm/llvm-project/commit/dc096a66cb519532121fb0fbedb13265bd4b29ec
DIFF: https://github.com/llvm/llvm-project/commit/dc096a66cb519532121fb0fbedb13265bd4b29ec.diff

LOG: [Diagnostics] Diagnose missing comma in string array initialization

Motivation (from PR37674):

const char *ss[] = {
  "foo", "bar",
  "baz", "qux"  // <-- Missing comma!
  "abc", "xyz"
  };

This kind of bug was recently also found in LLVM codebase (see PR47030).

Solves PR47038, PR37674

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D85545

Added: 
    clang/test/Sema/string-concat.c

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaExpr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 659f1d6df39e..7fe9396dbfc3 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3002,6 +3002,10 @@ def err_missing_atsign_prefix : Error<
 def warn_objc_string_literal_comparison : Warning<
   "direct comparison of a string literal has undefined behavior">,
   InGroup<ObjCStringComparison>;
+def warn_concatenated_literal_array_init : Warning<
+  "suspicious concatenation of string literals in an array initialization; "
+  "did you mean to separate the elements with a comma?">,
+  InGroup<DiagGroup<"string-concatenation">>;
 def warn_concatenated_nsarray_literal : Warning<
   "concatenated NSString literal for an NSArray expression - "
   "possibly missing a comma">,
@@ -6142,6 +6146,9 @@ def warn_overloaded_shift_in_comparison :Warning<
 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 the string literal to silence warning">;
+
 def warn_addition_in_bitshift : Warning<
   "operator '%0' has lower precedence than '%1'; "
   "'%1' will be evaluated first">, InGroup<ShiftOpParentheses>;

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 28da268e637f..e86c5b919698 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6906,6 +6906,21 @@ Sema::ActOnInitList(SourceLocation LBraceLoc, MultiExprArg InitArgList,
         << DIE->getSourceRange();
       Diag(InitArgList[I]->getBeginLoc(), diag::note_designated_init_mixed)
         << InitArgList[I]->getSourceRange();
+    } else if (const auto *SL = dyn_cast<StringLiteral>(InitArgList[I])) {
+      unsigned NumConcat = SL->getNumConcatenated();
+      // 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 > 1 && E > 1 && !SL->getBeginLoc().isMacroID()) {
+        SmallVector<FixItHint, 1> 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);
+      }
     }
   }
 

diff  --git a/clang/test/Sema/string-concat.c b/clang/test/Sema/string-concat.c
new file mode 100644
index 000000000000..bf8369b079c8
--- /dev/null
+++ b/clang/test/Sema/string-concat.c
@@ -0,0 +1,102 @@
+
+// RUN: %clang_cc1 -x c -fsyntax-only -verify %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s
+
+const char *missing_comma[] = {
+    "basic_filebuf",
+    "basic_ios",
+    "future",
+    "optional",
+    "packaged_task" // expected-note{{place parentheses around the string literal to silence warning}}
+    "promise",      // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}     
+    "shared_future"
+};
+
+#ifndef __cplusplus
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+const wchar_t *missing_comma_wchar[] = {
+    L"basic_filebuf",
+    L"packaged_task" // expected-note{{place parentheses around the string literal to silence warning}}
+    L"promise"      // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+};
+
+const char *missing_comma_u8[] = {
+    u8"basic_filebuf",
+    u8"packaged_task" // expected-note{{place parentheses around the string literal to silence warning}}
+    u8"promise"      // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+};
+
+const char *missing_two_commas[] = {"basic_filebuf",
+                       "basic_ios" // expected-note{{place parentheses around the string literal to silence warning}}
+                       "future"    // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+                       "optional",
+                       "packaged_task"};
+
+const char *missing_comma_same_line[] = {"basic_filebuf", "basic_ios",
+                       "future" "optional",         // expected-note{{place parentheses around the string literal to silence warning}}
+                       "packaged_task", "promise"}; // expected-warning at -1{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+
+const char *missing_comma_
diff erent_lines[] = {"basic_filebuf", "basic_ios" // expected-note{{place parentheses around the string literal to silence warning}}
+                       "future", "optional",        // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+                       "packaged_task", "promise"};
+
+const char *missing_comma_same_line_all_literals[] = {"basic_filebuf", "future" "optional", "packaged_task"}; // expected-note{{place parentheses around the string literal to silence warning}}
+                                                                               // expected-warning at -1{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+
+char missing_comma_inner[][4] = {
+    "a",
+    "b" // expected-note{{place parentheses around the string literal to silence warning}}
+    "c" // expected-warning{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with 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 the string literal to silence warning}}
+                           };          // expected-warning at -1{{suspicious concatenation of string literals in an array initialization; did you mean to separate the elements with a comma?}}
+
+// Do not warn for macros.
+
+#define BASIC_IOS "basic_ios"
+#define FUTURE "future"
+const char *macro_test2[] = {"basic_filebuf", BASIC_IOS
+                        FUTURE, "optional",
+                       "packaged_task", "promise"};
+
+#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" };
+
+// Do not warn when all the elements in the initializer are concatenated together.
+const char *all_elems_in_init_concatenated[] = {"a" "b" "c"};
+
+// 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"
+};


        


More information about the cfe-commits mailing list