[cfe-dev] Trouble using TextDiagnosticPrinter

Billy O'Mahony via cfe-dev cfe-dev at lists.llvm.org
Fri Oct 16 01:36:58 PDT 2020


Hi Ray,

thanks, I finally did notice that the ASTContext's DiagEngine already did
have a DiagConsumer registered at that one did work fine. But I had to add
a Builder.setForceEmit call. Of course by the time I had tried that I had
already added my own DiagConsumer so I was already well down a dead-end
path!

BTW I had already tried the matcher pattern for my analyzer but I found a
problem with the matchers traversal order (which was important to my
application as it keeps track of the order in which certain things occur
within the functions being analysed).
https://bugs.llvm.org/show_bug.cgi?id=46423 - there hasn't been any
activity on it but some of the developers had sketched out potential fixes
so maybe it has been addressed in the meantime.

Cheers,
Billy.

On Wed, 14 Oct 2020 at 20:34, Ray Zhang <peifeng2005 at gmail.com> 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> 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
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20201016/6abff066/attachment.html>


More information about the cfe-dev mailing list