From: Douglas Gregor Date: Tue, 10 Mar 2009 18:03:33 +0000 (+0000) Subject: Add a notion of "post-diagnostic hooks", which are callbacks attached X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ee1828a6b5ae1bc4ea300e48f3840ac1ec5be295;p=clang Add a notion of "post-diagnostic hooks", which are callbacks attached 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 --- diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 1bb294e7a4..3675e69212 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -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 diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 543391f292..dbba5ab18a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -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") diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 893eae5d1a..fa5ca57dfe 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -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; } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 1acd6b2a85..c662301da3 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 0990057df4..4a12b07766 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -21,6 +21,10 @@ 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(Cookie)->PrintInstantiationStack(); +} + +/// \brief Prints the current instantiation stack through a series of +/// notes. +void Sema::PrintInstantiationStack() { + for (llvm::SmallVector::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 index 0000000000..4c8ea13a8d --- /dev/null +++ b/test/SemaTemplate/instantiation-backtrace.cpp @@ -0,0 +1,16 @@ +// RUN: clang -fsyntax-only -verify %s +template struct A; // expected-note{{template is declared here}} + +template struct B : A { }; // expected-error{{implicit instantiation of undefined template}} + +template struct C : B { } ; // expected-note{{instantiation of template class}} + +template struct D : C { }; // expected-note{{instantiation of template class}} + +template struct E : D { }; // expected-note{{instantiation of template class}} + +template struct F : E { }; // expected-note{{instantiation of template class}} + +void f() { + (void)sizeof(F); // expected-note{{instantiation of template class}} +} diff --git a/test/SemaTemplate/instantiation-depth.cpp b/test/SemaTemplate/instantiation-depth.cpp index 3b8acf20dd..25c40fce4d 100644 --- a/test/SemaTemplate/instantiation-depth.cpp +++ b/test/SemaTemplate/instantiation-depth.cpp @@ -1,8 +1,9 @@ // RUN: clang -fsyntax-only -ftemplate-depth=5 -verify %s template struct X : X { }; // 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); + (void)sizeof(X); // expected-note {{instantiation of template class}} }