[cfe-dev] Trouble using TextDiagnosticPrinter

Andrzej Warzynski via cfe-dev cfe-dev at lists.llvm.org
Thu Oct 15 01:50:29 PDT 2020


Billy,

Are you more interested in:
   * understanding the Diagnostics API within Clang? or
   * writing a tool that can emit diagnostics?

The former will be much easier and straightforward if you use the 
clang::tooling API. Please, see example here:
* 
https://github.com/banach-space/clang-tutor/blob/master/tools/CodeStyleCheckerMain.cpp#L45-L47
With clang::tooling, DiagnosticsEngine and DiagnosticConsumer are set 
and managed for you behind the scenes (i.e. you shouldn't need to do 
anything extra). If you check a backtrace in your debugger, you'll 
notice that the driver within libclangFrontend manages that for you 
(i.e. creates a CompilerInstance, which contains DiagnosticEngine, which 
contains the default DiagnosticConsumer -> more or less ;-) ).

However, if you _are_ interested in the diagnostics APIs, then like Ray 
has highlighted - managing the resources is often the root cause of most 
issues. These APIs are quite complex though and this can take a fair bit 
of trial and error. Having said that, do upload your code on GitHub and 
I can take a look if you want.

-Andrzej

On 14/10/2020 20:33, Ray Zhang via cfe-dev wrote:
> Hi Billy,
> 
> I've also run into this issue but I skirted around it by creating a 
> FixItRewriter 
> <https://clang.llvm.org/doxygen/classclang_1_1FixItRewriter.html> which 
> is actually a DiagnosticConsumer which the diagnostics engine DOESN'T 
> own. Unsure if this is the same case that you're running into, but when 
> I was experiencing segfaults I found it to be an issue with lifetime 
> management for diagnostic consumers. YMMV since the issue is within the 
> FixItRewriter impl itself, but the class stores a pointer to the current 
> diagnostic engine's client before setting the instance of FixItRewriter 
> as our own. If we tell the engine to own the FixItRewriter, then the 
> previous consumer is destroyed, and our FixItRewriter is then pointing 
> to a destroyed resource. Can you try creating a std::unique_ptr for your 
> own diagnostics consumer and tell the engine not to own it? You'd have 
> to take care of the storage lifetime in this case.
> 
> Also, one more thing is, when the Builder object gets destroyed it 
> performs an Emit(), and if you previously Emit then it won't do a 
> double-write. At the closing curly brace of your VisitFunctionDecl, it 
> may be a good idea to check which consumer you are currently using. I 
> did a GDB backtrace in my situation at the end-curly-brace and found 
> that the diagnostics consumer used to emit the message was actually not 
> the one I thought I was using.
> 
> Since I didn't use the Visitor pattern but rather the Matcher pattern, 
> my scope of how the two use cases differ are limited. Hope you find the bug!
> 
> Best,
> Ray
> 
> On Wed, Oct 14, 2020 at 12:17 PM Billy O'Mahony via cfe-dev 
> <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
> 
>     Hi,
> 
>     After much hacking I managed to write a DiagnosticConsumer that
>     would emit some error messages for me. Kudos to Andrzej for his help.
> 
>     I was expecting that I would more or less automatically get
>     filename, line number and some source location carets added and some
>     nice coloured text on my console. So for that I think I might need
>     to use TextDiagnosticPrinter? However when I try to setClient that
>     in my ASTContext's diagEngine it segfaults when a diagnostic is emitted.
> 
>     I've tried looking in the clang/tools files for inspiration from
>     other FrontendAction style tools but they either don't use
>     TextDiagnosticPrinter or else they use some other clang infra
>     like clang/Rewrite/Core/Rewriter.h.
> 
>     This really feels like it should be super simple but I'm finding it
>     very frustrating. My tool is actually doing useful things to spot
>     project-specific code defects but now adding something simple like
>     neat error messages is turning into a total quagmire.
> 
>     Thanks,
>     Billy.
> 
>     class MyDiagnosticConsumer : public clang::DiagnosticConsumer {
>     public:
>          void HandleDiagnostic(clang::DiagnosticsEngine::Level
>     DiagLevel, const clang::Diagnostic& Info) override {
>              llvm::SmallVector<char, 512> message;
>              Info.FormatDiagnostic(message);
>              llvm::errs() << message << '\n';
>               cout << "Hello HandleDiagnostic!" << endl;
>          }
>     };
> 
>     class MyVisitor : public RecursiveASTVisitor<MyVisitor> {
>     public:
>        explicit MyVisitor(ASTContext *context)
>          : mContext(context) {}
> 
>        bool VisitFunctionDecl(FunctionDecl* fnDecl) {
>            // let's just issue an error on every function decl!
> 
>            auto& diagEngine = mContext->getDiagnostics();
>            const auto ID =
>     diagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
>                                                       "%0 declared? You
>     insensitive clod!");
>            auto Builder = diagEngine.Report(fnDecl->getLocation(), ID);
>            Builder.AddString(fnDecl->getNameAsString());
>           
>     Builder.AddSourceRange(clang::CharSourceRange::getCharRange(call->getSourceRange()));
>            Builder.setForceEmit();  // <<< without this
>     MyDiagnosticConsumer::HandleDiagnostic is never called !!
> 
>            return true;
>        }
> 
>     private:
>        ASTContext *mContext;
>     };
> 
> 
>     class MyConsumer : public clang::ASTConsumer {
>     public:
>        explicit MyConsumer(ASTContext *Context) : Visitor(Context) {
> 
>            DiagnosticsEngine &diagEngine = Context->getDiagnostics();
> 
>            // if I set my own DiagnosticsConsumer here it works (but no
>     line/file info).
>            diagEngine.setClient(new MyDiagnosticConsumer(),
>     /*ShouldOwnClient=*/true);
> 
>            ---- OR ----
> 
>            // if I set a TextDiagnosticPrinter it crashes later on
>            // At the point where I otherwise get an error like
>     '.../include/bla.h' file not found.
>            IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new
>     DiagnosticOptions();
>            TextDiagnosticPrinter *DiagClient =
>                new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
>            diagEngine.setClient(DiagClient, true); // true =>
>     shouldOwnClient
>        }
> 
>        virtual void HandleTranslationUnit(clang::ASTContext &Context) {
>            auto Decls = Context.getTranslationUnitDecl()->decls();
>            auto &SM = Context.getSourceManager();
>            for (auto &Decl : Decls) {
>                const auto& FileID = SM.getFileID(Decl->getLocation());
>                if (FileID != SM.getMainFileID()) {
>                    // Skip decls coming via #incl
>                    continue;
>                }
> 
>                Visitor.TraverseDecl(Decl);
>            }
>        }
> 
>     private:
>        MyVisitor Visitor;
>     };
> 
> 
> 
> 
>     Program received signal SIGSEGV, Segmentation fault.
>     clang::DiagnosticRenderer::emitDiagnostic (this=0x0, Loc=...,
>     Level=clang::DiagnosticsEngine::Fatal, Message=..., Ranges=...,
>          FixItHints=..., D=...) at
>     /home/bomahony/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp:95
>     95        beginDiagnostic(D, Level);
>     (gdb) bt
>     #0  clang::DiagnosticRenderer::emitDiagnostic (this=0x0, Loc=...,
>     Level=clang::DiagnosticsEngine::Fatal, Message=..., Ranges=...,
>          FixItHints=..., D=...) at
>     /home/bomahony/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp:95
>     #1  0x00000000084ce036 in
>     clang::TextDiagnosticPrinter::HandleDiagnostic (this=0x9b22430,
>     Level=clang::DiagnosticsEngine::Fatal, Info=...)
>          at
>     /home/bomahony/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp:152
> 
> 
>     _______________________________________________
>     cfe-dev mailing list
>     cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>     https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
> 
> 
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
> 


More information about the cfe-dev mailing list