Error if an anonymous DiagGroup is referenced multiple times.

Not only is this inefficient for TableGen, it's annoying for maintenance
when renaming warning flags (unusual) or adding those flags to a group
(more likely).

This uses the new fix-it infrastructure for LLVM's SourceMgr/SMDiagnostic,
as well as a few changes to TableGen to track more source information.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172087 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/utils/TableGen/ClangDiagnosticsEmitter.cpp b/utils/TableGen/ClangDiagnosticsEmitter.cpp
index b7b8e09..448fb70 100644
--- a/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -14,8 +14,12 @@
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Twine.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/TableGen/Error.h"
@@ -127,14 +131,41 @@
     std::vector<const Record*> DiagsInGroup;
     std::vector<std::string> SubGroups;
     unsigned IDNo;
+
+    const Record *ExplicitDef;
+
+    GroupInfo() : ExplicitDef(0) {}
   };
 } // end anonymous namespace.
 
+static bool beforeThanCompare(const Record *LHS, const Record *RHS) {
+  assert(!LHS->getLoc().empty() && !RHS->getLoc().empty());
+  return
+    LHS->getLoc().front().getPointer() < RHS->getLoc().front().getPointer();
+}
+
+static bool beforeThanCompareGroups(const GroupInfo *LHS, const GroupInfo *RHS){
+  assert(!LHS->DiagsInGroup.empty() && !RHS->DiagsInGroup.empty());
+  return beforeThanCompare(LHS->DiagsInGroup.front(),
+                           RHS->DiagsInGroup.front());
+}
+
+static SMRange findSuperClassRange(const Record *R, StringRef SuperName) {
+  ArrayRef<Record *> Supers = R->getSuperClasses();
+
+  for (size_t i = 0, e = Supers.size(); i < e; ++i)
+    if (Supers[i]->getName() == SuperName)
+      return R->getSuperClassRanges()[i];
+
+  return SMRange();
+}
+
 /// \brief Invert the 1-[0/1] mapping of diags to group into a one to many
 /// mapping of groups to diags in the group.
 static void groupDiagnostics(const std::vector<Record*> &Diags,
                              const std::vector<Record*> &DiagGroups,
                              std::map<std::string, GroupInfo> &DiagsInGroup) {
+
   for (unsigned i = 0, e = Diags.size(); i != e; ++i) {
     const Record *R = Diags[i];
     DefInit *DI = dyn_cast<DefInit>(R->getValueInit("Group"));
@@ -144,13 +175,25 @@
     std::string GroupName = DI->getDef()->getValueAsString("GroupName");
     DiagsInGroup[GroupName].DiagsInGroup.push_back(R);
   }
-  
+
+  typedef SmallPtrSet<GroupInfo *, 16> GroupSetTy;
+  GroupSetTy ImplicitGroups;
+
   // Add all DiagGroup's to the DiagsInGroup list to make sure we pick up empty
   // groups (these are warnings that GCC supports that clang never produces).
   for (unsigned i = 0, e = DiagGroups.size(); i != e; ++i) {
     Record *Group = DiagGroups[i];
     GroupInfo &GI = DiagsInGroup[Group->getValueAsString("GroupName")];
-    
+    if (Group->isAnonymous()) {
+      if (GI.DiagsInGroup.size() > 1)
+        ImplicitGroups.insert(&GI);
+    } else {
+      if (GI.ExplicitDef)
+        assert(GI.ExplicitDef == Group);
+      else
+        GI.ExplicitDef = Group;
+    }
+
     std::vector<Record*> SubGroups = Group->getValueAsListOfDefs("SubGroups");
     for (unsigned j = 0, e = SubGroups.size(); j != e; ++j)
       GI.SubGroups.push_back(SubGroups[j]->getValueAsString("GroupName"));
@@ -161,6 +204,80 @@
   for (std::map<std::string, GroupInfo>::iterator
        I = DiagsInGroup.begin(), E = DiagsInGroup.end(); I != E; ++I, ++IDNo)
     I->second.IDNo = IDNo;
+
+  // Sort the implicit groups, so we can warn about them deterministically.
+  SmallVector<GroupInfo *, 16> SortedGroups(ImplicitGroups.begin(),
+                                            ImplicitGroups.end());
+  for (SmallVectorImpl<GroupInfo *>::iterator I = SortedGroups.begin(),
+                                              E = SortedGroups.end();
+       I != E; ++I) {
+    MutableArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
+    std::sort(GroupDiags.begin(), GroupDiags.end(), beforeThanCompare);
+  }
+  std::sort(SortedGroups.begin(), SortedGroups.end(), beforeThanCompareGroups);
+
+  // Warn about the same group being used anonymously in multiple places.
+  for (SmallVectorImpl<GroupInfo *>::const_iterator I = SortedGroups.begin(),
+                                                    E = SortedGroups.end();
+       I != E; ++I) {
+    ArrayRef<const Record *> GroupDiags = (*I)->DiagsInGroup;
+
+    if ((*I)->ExplicitDef) {
+      std::string Name = (*I)->ExplicitDef->getValueAsString("GroupName");
+      for (ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
+                                                    DE = GroupDiags.end();
+           DI != DE; ++DI) {
+        const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
+        const Record *NextDiagGroup = GroupInit->getDef();
+        if (NextDiagGroup == (*I)->ExplicitDef)
+          continue;
+
+        SMRange InGroupRange = findSuperClassRange(*DI, "InGroup");
+        SmallString<64> Replacement;
+        if (InGroupRange.isValid()) {
+          Replacement += "InGroup<";
+          Replacement += (*I)->ExplicitDef->getName();
+          Replacement += ">";
+        }
+        SMFixIt FixIt(InGroupRange, Replacement.str());
+
+        SrcMgr.PrintMessage(NextDiagGroup->getLoc().front(),
+                            SourceMgr::DK_Error,
+                            Twine("group '") + Name +
+                              "' is referred to anonymously",
+                            ArrayRef<SMRange>(),
+                            InGroupRange.isValid() ? FixIt
+                                                   : ArrayRef<SMFixIt>());
+        SrcMgr.PrintMessage((*I)->ExplicitDef->getLoc().front(),
+                            SourceMgr::DK_Note, "group defined here");
+      }
+    } else {
+      // If there's no existing named group, we should just warn once and use
+      // notes to list all the other cases.
+      ArrayRef<const Record *>::const_iterator DI = GroupDiags.begin(),
+                                               DE = GroupDiags.end();
+      assert(DI != DE && "We only care about groups with multiple uses!");
+
+      const DefInit *GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
+      const Record *NextDiagGroup = GroupInit->getDef();
+      std::string Name = NextDiagGroup->getValueAsString("GroupName");
+
+      SMRange InGroupRange = findSuperClassRange(*DI, "InGroup");
+      SrcMgr.PrintMessage(NextDiagGroup->getLoc().front(),
+                          SourceMgr::DK_Error,
+                          Twine("group '") + Name +
+                            "' is referred to anonymously",
+                          InGroupRange);
+
+      for (++DI; DI != DE; ++DI) {
+        GroupInit = cast<DefInit>((*DI)->getValueInit("Group"));
+        InGroupRange = findSuperClassRange(*DI, "InGroup");
+        SrcMgr.PrintMessage(GroupInit->getDef()->getLoc().front(),
+                            SourceMgr::DK_Note, "also referenced here",
+                            InGroupRange);
+      }
+    }
+  }
 }
 
 //===----------------------------------------------------------------------===//