[analyzer][UninitializedObjectChecker] New flag to ignore records based on it's fields

Based on a suggestion from @george.karpenkov.

In some cases, structs are used as unions with a help of a tag/kind field.
This patch adds a new string flag (a pattern), that is matched against the
fields of a record, and should a match be found, the entire record is ignored.

For more info refer to http://lists.llvm.org/pipermail/cfe-dev/2018-August/058906.html
and to the responses to that, especially http://lists.llvm.org/pipermail/cfe-dev/2018-August/059215.html.

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

llvm-svn: 342220
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 92e21f9..b632229 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -109,6 +109,10 @@
 static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
                                       CheckerContext &Context);
 
+/// Checks whether RD contains a field with a name or type name that matches
+/// \p Pattern.
+static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
+
 //===----------------------------------------------------------------------===//
 //                  Methods for UninitializedObjectChecker.
 //===----------------------------------------------------------------------===//
@@ -228,6 +232,12 @@
     return true;
   }
 
+  if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
+      shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
+    IsAnyFieldInitialized = true;
+    return false;
+  }
+
   bool ContainsUninitField = false;
 
   // Are all of this non-union's fields initialized?
@@ -442,6 +452,19 @@
   return false;
 }
 
+static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
+  llvm::Regex R(Pattern);
+
+  for (const FieldDecl *FD : RD->fields()) {
+    if (R.match(FD->getType().getAsString()))
+      return true;
+    if (R.match(FD->getName()))
+      return true;
+  }
+
+  return false;
+}
+
 std::string clang::ento::getVariableName(const FieldDecl *Field) {
   // If Field is a captured lambda variable, Field->getName() will return with
   // an empty string. We can however acquire it's name from the lambda's
@@ -472,10 +495,13 @@
   AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
   UninitObjCheckerOptions &ChOpts = Chk->Opts;
 
-  ChOpts.IsPedantic = AnOpts.getBooleanOption(
-      "Pedantic", /*DefaultVal*/ false, Chk);
-  ChOpts.ShouldConvertNotesToWarnings = AnOpts.getBooleanOption(
-      "NotesAsWarnings", /*DefaultVal*/ false, Chk);
+  ChOpts.IsPedantic =
+      AnOpts.getBooleanOption("Pedantic", /*DefaultVal*/ false, Chk);
+  ChOpts.ShouldConvertNotesToWarnings =
+      AnOpts.getBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk);
   ChOpts.CheckPointeeInitialization = AnOpts.getBooleanOption(
       "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
+  ChOpts.IgnoredRecordsWithFieldPattern =
+      AnOpts.getOptionAsString("IgnoreRecordsWithField",
+                               /*DefaultVal*/ "", Chk);
 }