]> granicus.if.org Git - clang/commitdiff
Add a notion of "post-diagnostic hooks", which are callbacks attached
authorDouglas Gregor <dgregor@apple.com>
Tue, 10 Mar 2009 18:03:33 +0000 (18:03 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 10 Mar 2009 18:03:33 +0000 (18:03 +0000)
to a diagnostic that will be invoked after the diagnostic (if it is
not suppressed). The hooks are allowed to produce additional
diagnostics (typically notes) that provide more information. We should
be able to use this to help diagnostic clients link notes back to the
diagnostic they clarify. Comments welcome; I'll write up documentation
and convert other clients (e.g., overload resolution failures) if
there are no screams of protest.

As the first client of post-diagnostic hooks, we now produce a
template instantiation backtrace when a failure occurs during template
instantiation. There's still more work to do to make this output
pretty, if that's even possible.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@66557 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Diagnostic.h
include/clang/Basic/DiagnosticSemaKinds.def
lib/Basic/Diagnostic.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplateInstantiate.cpp
test/SemaTemplate/instantiation-backtrace.cpp [new file with mode: 0644]
test/SemaTemplate/instantiation-depth.cpp

index 1bb294e7a488640754b28d98e4cc548ad737c8f4..3675e69212cdfd7255c7ed29efdcbb171d5a29c3 100644 (file)
@@ -123,6 +123,33 @@ public:
   }
 };
 
+/// \brief A hook function that will be invoked after we have
+/// completed processing of the current diagnostic.
+///
+/// Hook functions are typically used to emit further diagnostics
+/// (typically notes) that give more information about this
+/// diagnostic.
+struct PostDiagnosticHook {
+  /// \brief The type of the hook function itself.
+  ///
+  /// DiagID is the ID of the diagnostic to which the hook was
+  /// attached, and Cookie is a user-specified value that can be used
+  /// to store extra data for the hook.
+  typedef void (*HookTy)(unsigned DiagID, void *Cookie);
+
+  PostDiagnosticHook() {}
+
+  PostDiagnosticHook(HookTy Hook, void *Cookie) : Hook(Hook), Cookie(Cookie) {
+    assert(Hook && "No hook provided!");
+  }
+
+  /// \brief The hook function.
+  HookTy Hook;
+
+  /// \brief The cookie that will be passed along to the hook function.
+  void *Cookie;
+};
+
 /// Diagnostic - This concrete class is used by the front-end to report
 /// problems and issues.  It massages the diagnostics (e.g. handling things like
 /// "report warnings as errors" and passes them off to the DiagnosticClient for
@@ -144,7 +171,7 @@ public:
     ak_declarationname, // DeclarationName
     ak_nameddecl        // NamedDecl *
   };
-  
+
 private:  
   bool IgnoreAllWarnings;     // Ignore all warnings: -w
   bool WarningsAsErrors;      // Treat warnings like errors: 
@@ -282,6 +309,10 @@ public:
   /// call on NOTEs.
   static bool isBuiltinWarningOrExtension(unsigned DiagID);
 
+  /// \brief Determine whether the given built-in diagnostic ID is a
+  /// Note.
+  static bool isBuiltinNote(unsigned DiagID);
+
   /// getDiagnosticLevel - Based on the way the client configured the Diagnostic
   /// object, classify the specified diagnostic ID into a Level, consumable by
   /// the DiagnosticClient.
@@ -330,7 +361,10 @@ private:
   /// \brief The number of code modifications hints in the
   /// CodeModificationHints array.
   unsigned char NumCodeModificationHints;
-  
+  /// \brief The number of post-diagnostic hooks in the
+  /// PostDiagnosticHooks array.
+  unsigned char NumPostDiagnosticHooks;
+
   /// DiagArgumentsKind - This is an array of ArgumentKind::ArgumentKind enum
   /// values, with one for each argument.  This specifies whether the argument
   /// is in DiagArgumentsStr or in DiagArguments.
@@ -357,6 +391,15 @@ private:
   /// to insert, remove, or modify at a particular position.
   CodeModificationHint CodeModificationHints[MaxCodeModificationHints];
 
+  enum { MaxPostDiagnosticHooks = 10 };
+  
+  /// \brief Functions that will be invoked after the diagnostic has
+  /// been emitted.
+  PostDiagnosticHook PostDiagnosticHooks[MaxPostDiagnosticHooks];
+
+  /// \brief Whether we're already within a post-diagnostic hook.
+  bool InPostDiagnosticHook;
+
   /// ProcessDiag - This is the method used to report a diagnostic that is
   /// finally fully formed.
   void ProcessDiag();
@@ -379,15 +422,16 @@ private:
 /// for example.
 class DiagnosticBuilder {
   mutable Diagnostic *DiagObj;
-  mutable unsigned NumArgs, NumRanges, NumCodeModificationHints;
+  mutable unsigned NumArgs, NumRanges, NumCodeModificationHints, 
+                   NumPostDiagnosticHooks;
   
   void operator=(const DiagnosticBuilder&); // DO NOT IMPLEMENT
   friend class Diagnostic;
   explicit DiagnosticBuilder(Diagnostic *diagObj)
     : DiagObj(diagObj), NumArgs(0), NumRanges(0), 
-      NumCodeModificationHints(0) {}
-public:
-  
+      NumCodeModificationHints(0), NumPostDiagnosticHooks(0) {}
+
+public:  
   /// Copy constructor.  When copied, this "takes" the diagnostic info from the
   /// input and neuters it.
   DiagnosticBuilder(const DiagnosticBuilder &D) {
@@ -405,6 +449,7 @@ public:
     DiagObj->NumDiagArgs = NumArgs;
     DiagObj->NumDiagRanges = NumRanges;
     DiagObj->NumCodeModificationHints = NumCodeModificationHints;
+    DiagObj->NumPostDiagnosticHooks = NumPostDiagnosticHooks;
 
     // Process the diagnostic, sending the accumulated information to the
     // DiagnosticClient.
@@ -445,6 +490,15 @@ public:
            "Too many code modification hints!");
     DiagObj->CodeModificationHints[NumCodeModificationHints++] = Hint;
   }
+
+  void AddPostDiagnosticHook(const PostDiagnosticHook &Hook) const {
+    assert(!DiagObj->InPostDiagnosticHook &&
+           "Can't add a post-diagnostic hook to a diagnostic inside "
+           "a post-diagnostic hook");
+    assert(NumPostDiagnosticHooks < Diagnostic::MaxPostDiagnosticHooks &&
+           "Too many post-diagnostic hooks");
+    DiagObj->PostDiagnosticHooks[NumPostDiagnosticHooks++] = Hook;
+  }
 };
 
 inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
@@ -494,6 +548,12 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
   DB.AddCodeModificationHint(Hint);
   return DB;
 }
+
+inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
+                                           const PostDiagnosticHook &Hook) {
+  DB.AddPostDiagnosticHook(Hook);
+  return DB;
+}
   
 
 /// Report - Issue the message to the client.  DiagID is a member of the
index 543391f292f8a14c43c174a907626f8752bf1e12..dbba5ab18abf941ee35e652b783c84b1dd019f7b 100644 (file)
@@ -647,6 +647,8 @@ DIAG(note_template_recursion_depth, NOTE,
      "instantiation depth")
 DIAG(err_template_implicit_instantiate_undefined, ERROR,
      "implicit instantiation of undefined template %0")
+DIAG(note_template_class_instantiation_here, NOTE,
+     "in instantiation of template class %0 requested here")
 
 DIAG(err_unexpected_typedef, ERROR,
      "unexpected type name %0: expected expression")
index 893eae5d1a97d6f3338e705a5f7f4a1fddc722fe..fa5ca57dfe68b5d6423965b5d967e19a3317c1d2 100644 (file)
@@ -201,6 +201,8 @@ Diagnostic::Diagnostic(DiagnosticClient *client) : Client(client) {
   
   ArgToStringFn = DummyArgToStringFn;
   ArgToStringCookie = 0;
+
+  InPostDiagnosticHook = false;
 }
 
 Diagnostic::~Diagnostic() {
@@ -225,6 +227,12 @@ bool Diagnostic::isBuiltinWarningOrExtension(unsigned DiagID) {
   return DiagID < diag::DIAG_UPPER_LIMIT && getBuiltinDiagClass(DiagID) < ERROR;
 }
 
+/// \brief Determine whether the given built-in diagnostic ID is a
+/// Note.
+bool Diagnostic::isBuiltinNote(unsigned DiagID) {
+  return DiagID < diag::DIAG_UPPER_LIMIT && getBuiltinDiagClass(DiagID) == NOTE;
+}
+
 
 /// getDescription - Given a diagnostic ID, return a description of the
 /// issue.
@@ -373,6 +381,16 @@ void Diagnostic::ProcessDiag() {
   // Finally, report it.
   Client->HandleDiagnostic(DiagLevel, Info);
   if (Client->IncludeInDiagnosticCounts()) ++NumDiagnostics;
+
+  // Invoke any post-diagnostic hooks.
+  unsigned LastDiag = CurDiagID;
+  CurDiagID = ~0U;
+  
+  InPostDiagnosticHook = true;
+  for (unsigned Hook = 0; Hook < NumPostDiagnosticHooks; ++Hook)
+    PostDiagnosticHooks[Hook].Hook(LastDiag, 
+                                   PostDiagnosticHooks[Hook].Cookie);
+  InPostDiagnosticHook = false;
 }
 
 
index 1acd6b2a85cae20e8304ad1a9932b024606a859e..c662301da3445536cf283590ced3e6b806fb4e04 100644 (file)
@@ -242,7 +242,10 @@ public:
 
   /// The primitive diagnostic helpers.
   DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) {
-    return Diags.Report(FullSourceLoc(Loc, SourceMgr), DiagID);
+    DiagnosticBuilder DB = Diags.Report(FullSourceLoc(Loc, SourceMgr), DiagID);
+    if (!Diags.isBuiltinNote(DiagID) && !ActiveTemplateInstantiations.empty())
+      DB << PostDiagnosticHook(PrintInstantiationStackHook, this);
+    return DB;
   }
 
   virtual void DeleteExpr(ExprTy *E);
@@ -1719,6 +1722,9 @@ public:
     operator=(const InstantiatingTemplate&); // not implemented
   };
 
+  static void PrintInstantiationStackHook(unsigned DiagID, void *Cookie);
+  void PrintInstantiationStack();
+
   QualType InstantiateType(QualType T, const TemplateArgument *TemplateArgs,
                            unsigned NumTemplateArgs,
                            SourceLocation Loc, DeclarationName Entity);
index 0990057df4f16e375f3182292222627493e2d307..4a12b077666ee50f8037d8e48bbaf066c5cbe586 100644 (file)
 
 using namespace clang;
 
+//===----------------------------------------------------------------------===/
+// Template Instantiation Support
+//===----------------------------------------------------------------------===/
+
 Sema::InstantiatingTemplate::
 InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
                       ClassTemplateSpecializationDecl *Entity,
@@ -50,6 +54,26 @@ Sema::InstantiatingTemplate::~InstantiatingTemplate() {
     SemaRef.ActiveTemplateInstantiations.pop_back();
 }
 
+/// \brief Post-diagnostic hook for printing the instantiation stack.
+void Sema::PrintInstantiationStackHook(unsigned, void *Cookie) {
+  static_cast<Sema*>(Cookie)->PrintInstantiationStack();
+}
+
+/// \brief Prints the current instantiation stack through a series of
+/// notes.
+void Sema::PrintInstantiationStack() {
+  for (llvm::SmallVector<ActiveTemplateInstantiation, 16>::reverse_iterator
+         Active = ActiveTemplateInstantiations.rbegin(),
+         ActiveEnd = ActiveTemplateInstantiations.rend();
+       Active != ActiveEnd;
+       ++Active) {
+    Diags.Report(FullSourceLoc(Active->PointOfInstantiation, SourceMgr), 
+                 diag::note_template_class_instantiation_here)
+      << Context.getTypeDeclType(Active->Entity)
+      << Active->InstantiationRange;
+  }
+}
+
 //===----------------------------------------------------------------------===/
 // Template Instantiation for Types
 //===----------------------------------------------------------------------===/
diff --git a/test/SemaTemplate/instantiation-backtrace.cpp b/test/SemaTemplate/instantiation-backtrace.cpp
new file mode 100644 (file)
index 0000000..4c8ea13
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: clang -fsyntax-only -verify %s
+template<typename T> struct A; // expected-note{{template is declared here}}
+
+template<typename T> struct B : A<T*> { }; // expected-error{{implicit instantiation of undefined template}}
+
+template<typename T> struct C : B<T> { } ; // expected-note{{instantiation of template class}}
+
+template<typename T> struct D : C<T> { }; // expected-note{{instantiation of template class}}
+
+template<typename T> struct E : D<T> { }; // expected-note{{instantiation of template class}}
+
+template<typename T> struct F : E<T(T)> { }; // expected-note{{instantiation of template class}}
+
+void f() {
+ (void)sizeof(F<int>); // expected-note{{instantiation of template class}}
+}
index 3b8acf20dd1a304102afd81186fdb74f3aee6070..25c40fce4d1afafdadfd4d7f1ebb59162803ff51 100644 (file)
@@ -1,8 +1,9 @@
 // RUN: clang -fsyntax-only -ftemplate-depth=5 -verify %s
 
 template<typename T> struct X : X<T*> { }; // expected-error{{recursive template instantiation exceeded maximum depth of 5}} \
-// expected-note{{use -ftemplate-depth=N to increase recursive template instantiation depth}}
+// expected-note{{use -ftemplate-depth=N to increase recursive template instantiation depth}} \
+// expected-note 5 {{instantiation of template class}}
 
 void test() { 
-  (void)sizeof(X<int>);
+  (void)sizeof(X<int>); // expected-note {{instantiation of template class}}
 }