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 {
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 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