[cfe-commits] r172087 - in /cfe/trunk: test/TableGen/ test/TableGen/DiagnosticBase.inc test/TableGen/anonymous-groups.td test/TableGen/lit.local.cfg test/TableGen/tg-fixits.td utils/TableGen/ClangDiagnosticsEmitter.cpp

Jordan Rose jordan_rose at apple.com
Thu Jan 10 10:50:46 PST 2013


Author: jrose
Date: Thu Jan 10 12:50:46 2013
New Revision: 172087

URL: http://llvm.org/viewvc/llvm-project?rev=172087&view=rev
Log:
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.

Added:
    cfe/trunk/test/TableGen/
    cfe/trunk/test/TableGen/DiagnosticBase.inc
    cfe/trunk/test/TableGen/anonymous-groups.td
    cfe/trunk/test/TableGen/lit.local.cfg
    cfe/trunk/test/TableGen/tg-fixits.td
Modified:
    cfe/trunk/utils/TableGen/ClangDiagnosticsEmitter.cpp

Added: cfe/trunk/test/TableGen/DiagnosticBase.inc
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/TableGen/DiagnosticBase.inc?rev=172087&view=auto
==============================================================================
--- cfe/trunk/test/TableGen/DiagnosticBase.inc (added)
+++ cfe/trunk/test/TableGen/DiagnosticBase.inc Thu Jan 10 12:50:46 2013
@@ -0,0 +1,35 @@
+// Define the diagnostic mappings.
+class DiagMapping;
+def MAP_IGNORE  : DiagMapping;
+def MAP_WARNING : DiagMapping;
+def MAP_ERROR   : DiagMapping;
+def MAP_FATAL   : DiagMapping;
+
+// Define the diagnostic classes.
+class DiagClass;
+def CLASS_NOTE      : DiagClass;
+def CLASS_WARNING   : DiagClass;
+def CLASS_EXTENSION : DiagClass;
+def CLASS_ERROR     : DiagClass;
+
+class DiagGroup<string Name, list<DiagGroup> subgroups = []> {
+  string GroupName = Name;
+  list<DiagGroup> SubGroups = subgroups;
+  string CategoryName = "";
+}
+class InGroup<DiagGroup G> { DiagGroup Group = G; }
+
+// All diagnostics emitted by the compiler are an indirect subclass of this.
+class Diagnostic<string text, DiagClass DC, DiagMapping defaultmapping> {
+  string      Text = text;
+  DiagClass   Class = DC;
+  DiagMapping DefaultMapping = defaultmapping;
+  DiagGroup   Group;
+  string      CategoryName = "";
+}
+
+class Error<string str>     : Diagnostic<str, CLASS_ERROR, MAP_ERROR>;
+class Warning<string str>   : Diagnostic<str, CLASS_WARNING, MAP_WARNING>;
+class Extension<string str> : Diagnostic<str, CLASS_EXTENSION, MAP_IGNORE>;
+class ExtWarn<string str>   : Diagnostic<str, CLASS_EXTENSION, MAP_WARNING>;
+class Note<string str>      : Diagnostic<str, CLASS_NOTE, MAP_FATAL/*ignored*/>;

Added: cfe/trunk/test/TableGen/anonymous-groups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/TableGen/anonymous-groups.td?rev=172087&view=auto
==============================================================================
--- cfe/trunk/test/TableGen/anonymous-groups.td (added)
+++ cfe/trunk/test/TableGen/anonymous-groups.td Thu Jan 10 12:50:46 2013
@@ -0,0 +1,42 @@
+// RUN: clang-tblgen -gen-clang-diag-groups -I%S %s -o /dev/null 2>&1 | FileCheck --strict-whitespace %s
+include "DiagnosticBase.inc"
+
+// Do not move this line; it is referred to by absolute line number in the
+// FileCheck lines below.
+def NamedGroup : DiagGroup<"name">;
+
+
+def InNamedGroup : Warning<"">, InGroup<DiagGroup<"name">>;
+//      CHECK: anonymous-groups.td:[[@LINE-1]]:41: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^def InNamedGroup : Warning<"">, InGroup<DiagGroup<"name">>;}}
+// CHECK-NEXT: {{^                                ~~~~~~~~\^~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: {{^                                InGroup<NamedGroup>}}
+// CHECK-NEXT: anonymous-groups.td:6:1: note: group defined here
+// CHECK-NEXT: def NamedGroup : DiagGroup<"name">;
+// CHECK-NEXT: ^
+
+
+def AlsoInNamedGroup : Warning<"">, InGroup  < DiagGroup<"name"> >;
+//      CHECK: anonymous-groups.td:[[@LINE-1]]:48: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^def AlsoInNamedGroup : Warning<"">, InGroup  < DiagGroup<"name"> >;}}
+// CHECK-NEXT: {{^                                    ~~~~~~~~~~~\^~~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: {{^                                    InGroup<NamedGroup>}}
+// CHECK-NEXT: anonymous-groups.td:6:1: note: group defined here
+// CHECK-NEXT: def NamedGroup : DiagGroup<"name">;
+// CHECK-NEXT: ^
+
+
+def AnonymousGroup : Warning<"">, InGroup<DiagGroup<"anonymous">>;
+def AlsoAnonymousGroup : Warning<"">, InGroup<DiagGroup<"anonymous">>;
+def AnonymousGroupAgain : Warning<"">,
+  InGroup<DiagGroup<"anonymous">>;
+
+//      CHECK: anonymous-groups.td:[[@LINE-5]]:43: error: group 'anonymous' is referred to anonymously
+// CHECK-NEXT: {{^def AnonymousGroup : Warning<"">, InGroup<DiagGroup<"anonymous">>;}}
+// CHECK-NEXT: {{^                                  ~~~~~~~~\^~~~~~~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: anonymous-groups.td:[[@LINE-7]]:47: note: also referenced here
+// CHECK-NEXT: {{^def AlsoAnonymousGroup : Warning<"">, InGroup<DiagGroup<"anonymous">>;}}
+// CHECK-NEXT: {{^                                      ~~~~~~~~\^~~~~~~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: anonymous-groups.td:[[@LINE-8]]:11: note: also referenced here
+// CHECK-NEXT: {{^  InGroup<DiagGroup<"anonymous">>;}}
+// CHECK-NEXT: {{^  ~~~~~~~~\^~~~~~~~~~~~~~~~~~~~~~~}}

Added: cfe/trunk/test/TableGen/lit.local.cfg
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/TableGen/lit.local.cfg?rev=172087&view=auto
==============================================================================
--- cfe/trunk/test/TableGen/lit.local.cfg (added)
+++ cfe/trunk/test/TableGen/lit.local.cfg Thu Jan 10 12:50:46 2013
@@ -0,0 +1 @@
+config.suffixes = ['.td']

Added: cfe/trunk/test/TableGen/tg-fixits.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/TableGen/tg-fixits.td?rev=172087&view=auto
==============================================================================
--- cfe/trunk/test/TableGen/tg-fixits.td (added)
+++ cfe/trunk/test/TableGen/tg-fixits.td Thu Jan 10 12:50:46 2013
@@ -0,0 +1,41 @@
+// RUN: clang-tblgen -gen-clang-diag-groups -I%S %s -o /dev/null 2>&1 | FileCheck --strict-whitespace %s
+include "DiagnosticBase.inc"
+
+def NamedGroup : DiagGroup<"name">;
+
+def InNamedGroup : Warning<"">, InGroup<DiagGroup<"name">>;
+//      CHECK: tg-fixits.td:[[@LINE-1]]:41: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^def InNamedGroup : Warning<"">, InGroup<DiagGroup<"name">>;}}
+// CHECK-NEXT: {{^                                ~~~~~~~~\^~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: {{^                                InGroup<NamedGroup>}}
+
+def Wrapped : Warning<"">, InGroup<DiagGroup<
+  "name">>;
+//      CHECK: tg-fixits.td:[[@LINE-2]]:36: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^def Wrapped : Warning<"">, InGroup<DiagGroup<}}
+// CHECK-NEXT: {{^                           ~~~~~~~~\^~~~~~~~~~}}
+// CHECK-NEXT: {{^                           InGroup<NamedGroup>}}
+
+def AlsoWrapped : Warning<"">, InGroup<
+  DiagGroup<"name">>;
+//      CHECK: tg-fixits.td:[[@LINE-1]]:3: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^  DiagGroup<"name">>;}}
+// CHECK-NEXT: {{^~~\^~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: {{^InGroup<NamedGroup>}}
+
+// The following lines contain hard tabs (\t); do not change this!
+def HardTabs : Warning<"">,
+	InGroup<	DiagGroup<"name">	>;
+//      CHECK: tg-fixits.td:[[@LINE-1]]:11: error: group 'name' is referred to anonymously
+// CHECK-NEXT: {{^        InGroup<        DiagGroup<"name">       >;}}
+// CHECK-NEXT: {{^        ~~~~~~~~~~~~~~~~\^~~~~~~~~~~~~~~~~~~~~~~~~}}
+// CHECK-NEXT: {{^        InGroup<NamedGrop>}}
+
+// The following line has Unicode characters in it; do not change them!
+// FIXME: For now, we just give up on printing carets/ranges/fixits for
+// lines with Unicode in them, because SMDiagnostic don't keep a byte<->column
+// map around to line things up like Clang does.
+def Unicode : Warning<"ユニコード">, InGroup<DiagGroup<"name">>;
+//      CHECK: tg-fixits.td:[[@LINE-1]]:51: error: group 'name' is referred to anonymously
+// CHECK-NEXT: def Unicode : Warning<"{{[^"]+}}">, InGroup<DiagGroup<"name">>;
+// CHECK-NEXT: note:

Modified: cfe/trunk/utils/TableGen/ClangDiagnosticsEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangDiagnosticsEmitter.cpp?rev=172087&r1=172086&r2=172087&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangDiagnosticsEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangDiagnosticsEmitter.cpp Thu Jan 10 12:50:46 2013
@@ -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);
+      }
+    }
+  }
 }
 
 //===----------------------------------------------------------------------===//





More information about the cfe-commits mailing list