Allow predefined styles to define different options for different languages.

Summary:
Allow predefined styles to define different options for different
languages so that one can run:
  clang-format -style=google file1.cpp file2.js

or use a single .clang-format file with "BasedOnStyle: Google" for both c++ and
JS files.

Added Google style for JavaScript with "BreakBeforeTernaryOperators" set to
false.

Reviewers: djasper

Reviewed By: djasper

CC: cfe-commits, klimek

Differential Revision: http://llvm-reviews.chandlerc.com/D2364

llvm-svn: 196909
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index faaafc2..562e67d 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -6950,42 +6950,88 @@
   verifyFormat("#pragma omp reduction(+ : var)");
 }
 
-bool allStylesEqual(ArrayRef<FormatStyle> Styles) {
-  for (size_t i = 1; i < Styles.size(); ++i)
-    if (!(Styles[0] == Styles[i]))
-      return false;
-  return true;
-}
+#define EXPECT_ALL_STYLES_EQUAL(Styles)                                        \
+  for (size_t i = 1; i < Styles.size(); ++i)                                   \
+    EXPECT_EQ(Styles[0], Styles[i]) << "Style #" << i << " of "                \
+                                    << Styles.size()                           \
+                                    << " differs from Style #0"
 
 TEST_F(FormatTest, GetsPredefinedStyleByName) {
-  FormatStyle Styles[3];
+  SmallVector<FormatStyle, 3> Styles;
+  Styles.resize(3);
 
   Styles[0] = getLLVMStyle();
-  EXPECT_TRUE(getPredefinedStyle("LLVM", &Styles[1]));
-  EXPECT_TRUE(getPredefinedStyle("lLvM", &Styles[2]));
-  EXPECT_TRUE(allStylesEqual(Styles));
+  EXPECT_TRUE(getPredefinedStyle("LLVM", FormatStyle::LK_Cpp, &Styles[1]));
+  EXPECT_TRUE(getPredefinedStyle("lLvM", FormatStyle::LK_Cpp, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 
   Styles[0] = getGoogleStyle();
-  EXPECT_TRUE(getPredefinedStyle("Google", &Styles[1]));
-  EXPECT_TRUE(getPredefinedStyle("gOOgle", &Styles[2]));
-  EXPECT_TRUE(allStylesEqual(Styles));
+  EXPECT_TRUE(getPredefinedStyle("Google", FormatStyle::LK_Cpp, &Styles[1]));
+  EXPECT_TRUE(getPredefinedStyle("gOOgle", FormatStyle::LK_Cpp, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
+
+  Styles[0] = getGoogleJSStyle();
+  EXPECT_TRUE(
+      getPredefinedStyle("Google", FormatStyle::LK_JavaScript, &Styles[1]));
+  EXPECT_TRUE(
+      getPredefinedStyle("gOOgle", FormatStyle::LK_JavaScript, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 
   Styles[0] = getChromiumStyle();
-  EXPECT_TRUE(getPredefinedStyle("Chromium", &Styles[1]));
-  EXPECT_TRUE(getPredefinedStyle("cHRoMiUM", &Styles[2]));
-  EXPECT_TRUE(allStylesEqual(Styles));
+  EXPECT_TRUE(getPredefinedStyle("Chromium", FormatStyle::LK_Cpp, &Styles[1]));
+  EXPECT_TRUE(getPredefinedStyle("cHRoMiUM", FormatStyle::LK_Cpp, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 
   Styles[0] = getMozillaStyle();
-  EXPECT_TRUE(getPredefinedStyle("Mozilla", &Styles[1]));
-  EXPECT_TRUE(getPredefinedStyle("moZILla", &Styles[2]));
-  EXPECT_TRUE(allStylesEqual(Styles));
+  EXPECT_TRUE(getPredefinedStyle("Mozilla", FormatStyle::LK_Cpp, &Styles[1]));
+  EXPECT_TRUE(getPredefinedStyle("moZILla", FormatStyle::LK_Cpp, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 
   Styles[0] = getWebKitStyle();
-  EXPECT_TRUE(getPredefinedStyle("WebKit", &Styles[1]));
-  EXPECT_TRUE(getPredefinedStyle("wEbKit", &Styles[2]));
-  EXPECT_TRUE(allStylesEqual(Styles));
+  EXPECT_TRUE(getPredefinedStyle("WebKit", FormatStyle::LK_Cpp, &Styles[1]));
+  EXPECT_TRUE(getPredefinedStyle("wEbKit", FormatStyle::LK_Cpp, &Styles[2]));
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 
-  EXPECT_FALSE(getPredefinedStyle("qwerty", &Styles[0]));
+  EXPECT_FALSE(getPredefinedStyle("qwerty", FormatStyle::LK_Cpp, &Styles[0]));
+}
+
+TEST_F(FormatTest, GetsCorrectBasedOnStyle) {
+  SmallVector<FormatStyle, 8> Styles;
+  Styles.resize(2);
+
+  Styles[0] = getGoogleStyle();
+  Styles[1] = getLLVMStyle();
+  EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1]).value());
+  EXPECT_ALL_STYLES_EQUAL(Styles);
+
+  Styles.resize(5);
+  Styles[0] = getGoogleJSStyle();
+  Styles[1] = getLLVMStyle();
+  Styles[1].Language = FormatStyle::LK_JavaScript;
+  EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google", &Styles[1]).value());
+
+  Styles[2] = getLLVMStyle();
+  Styles[2].Language = FormatStyle::LK_JavaScript;
+  EXPECT_EQ(0, parseConfiguration("Language: JavaScript\n"
+                                  "BasedOnStyle: Google",
+                                  &Styles[2]).value());
+
+  Styles[3] = getLLVMStyle();
+  Styles[3].Language = FormatStyle::LK_JavaScript;
+  EXPECT_EQ(0, parseConfiguration("BasedOnStyle: Google\n"
+                                  "Language: JavaScript",
+                                  &Styles[3]).value());
+
+  Styles[4] = getLLVMStyle();
+  Styles[4].Language = FormatStyle::LK_JavaScript;
+  EXPECT_EQ(0, parseConfiguration("---\n"
+                                  "BasedOnStyle: LLVM\n"
+                                  "IndentWidth: 123\n"
+                                  "---\n"
+                                  "BasedOnStyle: Google\n"
+                                  "Language: JavaScript",
+                                  &Styles[4]).value());
+  EXPECT_ALL_STYLES_EQUAL(Styles);
 }
 
 #define CHECK_PARSE(TEXT, FIELD, VALUE)                                        \
@@ -7192,6 +7238,23 @@
   EXPECT_EQ(FormatStyle::LK_Cpp, Style.Language);
 }
 
+TEST_F(FormatTest, UsesLanguageForBasedOnStyle) {
+  FormatStyle Style = {};
+  Style.Language = FormatStyle::LK_JavaScript;
+  Style.BreakBeforeTernaryOperators = true;
+  CHECK_PARSE("BasedOnStyle: Google", BreakBeforeTernaryOperators, false);
+  Style.BreakBeforeTernaryOperators = true;
+  CHECK_PARSE("---\n"
+              "BasedOnStyle: Google\n"
+              "---\n"
+              "Language: JavaScript\n"
+              "IndentWidth: 76\n"
+              "...\n",
+              BreakBeforeTernaryOperators, false);
+  EXPECT_EQ(76u, Style.IndentWidth);
+  EXPECT_EQ(FormatStyle::LK_JavaScript, Style.Language);
+}
+
 #undef CHECK_PARSE
 #undef CHECK_PARSE_BOOL
 
diff --git a/clang/unittests/Format/FormatTestJS.cpp b/clang/unittests/Format/FormatTestJS.cpp
index 1f8f139..0d242a7 100644
--- a/clang/unittests/Format/FormatTestJS.cpp
+++ b/clang/unittests/Format/FormatTestJS.cpp
@@ -33,24 +33,18 @@
   }
 
   static std::string format(llvm::StringRef Code,
-                            const FormatStyle &Style = getJSStyle()) {
+                            const FormatStyle &Style = getGoogleJSStyle()) {
     return format(Code, 0, Code.size(), Style);
   }
 
-  static FormatStyle getJSStyle() {
-    FormatStyle Style = getLLVMStyle();
-    Style.Language = FormatStyle::LK_JavaScript;
-    return Style;
-  }
-
-  static FormatStyle getJSStyleWithColumns(unsigned ColumnLimit) {
-    FormatStyle Style = getJSStyle();
+  static FormatStyle getGoogleJSStyleWithColumns(unsigned ColumnLimit) {
+    FormatStyle Style = getGoogleJSStyle();
     Style.ColumnLimit = ColumnLimit;
     return Style;
   }
 
   static void verifyFormat(llvm::StringRef Code,
-                           const FormatStyle &Style = getJSStyle()) {
+                           const FormatStyle &Style = getGoogleJSStyle()) {
     EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
   }
 };
@@ -60,26 +54,30 @@
   verifyFormat("a != = b;");
 
   verifyFormat("a === b;");
-  verifyFormat("aaaaaaa ===\n    b;", getJSStyleWithColumns(10));
+  verifyFormat("aaaaaaa ===\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("a !== b;");
-  verifyFormat("aaaaaaa !==\n    b;", getJSStyleWithColumns(10));
+  verifyFormat("aaaaaaa !==\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("if (a + b + c +\n"
                "        d !==\n"
                "    e + f + g)\n"
                "  q();",
-               getJSStyleWithColumns(20));
+               getGoogleJSStyleWithColumns(20));
 
   verifyFormat("a >> >= b;");
 
   verifyFormat("a >>> b;");
-  verifyFormat("aaaaaaa >>>\n    b;", getJSStyleWithColumns(10));
+  verifyFormat("aaaaaaa >>>\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("a >>>= b;");
-  verifyFormat("aaaaaaa >>>=\n    b;", getJSStyleWithColumns(10));
+  verifyFormat("aaaaaaa >>>=\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("if (a + b + c +\n"
                "        d >>>\n"
                "    e + f + g)\n"
                "  q();",
-               getJSStyleWithColumns(20));
+               getGoogleJSStyleWithColumns(20));
+  verifyFormat("var x = aaaaaaaaaa ?\n"
+               "            bbbbbb :\n"
+               "            ccc;",
+               getGoogleJSStyleWithColumns(20));
 }
 
 } // end namespace tooling