[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