Fixes crasher bug in JSONCompilationDatabase for invalid input.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@156814 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp
index dd9ccc0..c87833f 100644
--- a/lib/Tooling/CompilationDatabase.cpp
+++ b/lib/Tooling/CompilationDatabase.cpp
@@ -222,10 +222,9 @@
       ErrorMessage = "Expected object.";
       return false;
     }
-    llvm::yaml::ScalarNode *Directory;
-    llvm::yaml::ScalarNode *Command;
-    llvm::SmallString<8> FileStorage;
-    llvm::StringRef File;
+    llvm::yaml::ScalarNode *Directory = NULL;
+    llvm::yaml::ScalarNode *Command = NULL;
+    llvm::yaml::ScalarNode *File = NULL;
     for (llvm::yaml::MappingNode::iterator KVI = Object->begin(),
                                            KVE = Object->end();
          KVI != KVE; ++KVI) {
@@ -242,20 +241,37 @@
       }
       llvm::yaml::ScalarNode *KeyString =
         llvm::dyn_cast<llvm::yaml::ScalarNode>((*KVI).getKey());
+      if (KeyString == NULL) {
+        ErrorMessage = "Expected strings as key.";
+        return false;
+      }
       llvm::SmallString<8> KeyStorage;
       if (KeyString->getValue(KeyStorage) == "directory") {
         Directory = ValueString;
       } else if (KeyString->getValue(KeyStorage) == "command") {
         Command = ValueString;
       } else if (KeyString->getValue(KeyStorage) == "file") {
-        File = ValueString->getValue(FileStorage);
+        File = ValueString;
       } else {
         ErrorMessage = ("Unknown key: \"" +
                         KeyString->getRawValue() + "\"").str();
         return false;
       }
     }
-    IndexByFile[File].push_back(
+    if (!File) {
+      ErrorMessage = "Missing key: \"file\".";
+      return false;
+    }
+    if (!Command) {
+      ErrorMessage = "Missing key: \"command\".";
+      return false;
+    }
+    if (!Directory) {
+      ErrorMessage = "Missing key: \"directory\".";
+      return false;
+    }
+    llvm::SmallString<8> FileStorage;
+    IndexByFile[File->getValue(FileStorage)].push_back(
       CompileCommandRef(Directory, Command));
   }
   return true;
diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp
index 68d2896..7753c15 100644
--- a/unittests/Tooling/CompilationDatabaseTest.cpp
+++ b/unittests/Tooling/CompilationDatabaseTest.cpp
@@ -18,6 +18,26 @@
 namespace clang {
 namespace tooling {
 
+static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
+  std::string ErrorMessage;
+  EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase,
+                                                          ErrorMessage))
+    << "Expected an error because of: " << Explanation;
+}
+
+TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
+  expectFailure("", "Empty database");
+  expectFailure("{", "Invalid JSON");
+  expectFailure("[[]]", "Array instead of object");
+  expectFailure("[{\"a\":[]}]", "Array instead of value");
+  expectFailure("[{\"a\":\"b\"}]", "Unknown key");
+  expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
+  expectFailure("[{}]", "Empty entry");
+  expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
+  expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
+  expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
+}
+
 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
                                                     StringRef JSONDatabase,
                                                     std::string &ErrorMessage) {