Index: clang/include/clang/Interpreter/Interpreter.h =================================================================== --- clang/include/clang/Interpreter/Interpreter.h +++ clang/include/clang/Interpreter/Interpreter.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H #define LLVM_CLANG_INTERPRETER_INTERPRETER_H +#include "clang/Basic/Diagnostic.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/AST/GlobalDecl.h" @@ -41,7 +42,8 @@ class IncrementalCompilerBuilder { public: static llvm::Expected> - create(std::vector &ClangArgv); + create(std::vector &ClangArgv, + std::unique_ptr DiagConsumer = nullptr); }; /// Provides top-level interfaces for incremental compilation and execution. Index: clang/lib/Interpreter/Interpreter.cpp =================================================================== --- clang/lib/Interpreter/Interpreter.cpp +++ clang/lib/Interpreter/Interpreter.cpp @@ -17,6 +17,7 @@ #include "IncrementalParser.h" #include "clang/AST/ASTContext.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" @@ -61,7 +62,8 @@ } static llvm::Expected> -CreateCI(const llvm::opt::ArgStringList &Argv) { +CreateCI(const llvm::opt::ArgStringList &Argv, + std::unique_ptr DiagConsumer) { std::unique_ptr Clang(new CompilerInstance()); IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); @@ -71,11 +73,10 @@ PCHOps->registerWriter(std::make_unique()); PCHOps->registerReader(std::make_unique()); - // Buffer diagnostics from argument parsing so that we can output them using - // a well formed diagnostic object. - IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); - TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; - DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + IntrusiveRefCntPtr DiagOpts = + CreateAndPopulateDiagOpts(Argv); + DiagnosticConsumer *TextBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, TextBuffer); bool Success = CompilerInvocation::CreateFromArgs( Clang->getInvocation(), llvm::makeArrayRef(Argv.begin(), Argv.size()), Diags); @@ -87,13 +88,18 @@ CompilerInvocation::GetResourcesPath(Argv[0], nullptr); // Create the actual diagnostics engine. - Clang->createDiagnostics(); + if (DiagConsumer) { + Clang->createDiagnostics(DiagConsumer.get()); + DiagConsumer.release(); + } else { + Clang->createDiagnostics(); + } + if (!Clang->hasDiagnostics()) return llvm::createStringError(llvm::errc::not_supported, "Initialization failed. " "Unable to create diagnostics engine"); - DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); if (!Success) return llvm::createStringError(llvm::errc::not_supported, "Initialization failed. " @@ -125,7 +131,9 @@ } // anonymous namespace llvm::Expected> -IncrementalCompilerBuilder::create(std::vector &ClangArgv) { +IncrementalCompilerBuilder::create( + std::vector &ClangArgv, + std::unique_ptr DiagConsumer) { // If we don't know ClangArgv0 or the address of main() at this point, try // to guess it anyway (it's possible on some platforms). @@ -155,8 +163,8 @@ IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); IntrusiveRefCntPtr DiagOpts = CreateAndPopulateDiagOpts(ClangArgv); - TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; - DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + DiagnosticConsumer *TextBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, TextBuffer); driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], llvm::sys::getProcessTriple(), Diags); @@ -171,7 +179,7 @@ if (auto Err = ErrOrCC1Args.takeError()) return std::move(Err); - return CreateCI(**ErrOrCC1Args); + return CreateCI(**ErrOrCC1Args, std::move(DiagConsumer)); } Interpreter::Interpreter(std::unique_ptr CI, Index: clang/tools/clang-repl/ClangRepl.cpp =================================================================== --- clang/tools/clang-repl/ClangRepl.cpp +++ clang/tools/clang-repl/ClangRepl.cpp @@ -11,10 +11,14 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticParse.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Interpreter/Interpreter.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/LineEditor/LineEditor.h" #include "llvm/Support/CommandLine.h" @@ -22,6 +26,8 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" // llvm::Initialize* +#include + static llvm::cl::list ClangArgs("Xcc", llvm::cl::desc("Argument to pass to the CompilerInvocation"), @@ -31,6 +37,75 @@ static llvm::cl::list OptInputs(llvm::cl::Positional, llvm::cl::desc("[code to run]")); +class IncrementalDiagnosticBuffer : public clang::DiagnosticConsumer { +public: + using DiagList = std::vector>; + using iterator = DiagList::iterator; + using const_iterator = DiagList::const_iterator; + +public: + IncrementalDiagnosticBuffer() : TextStream(TextBuffer) {} + + ~IncrementalDiagnosticBuffer() = default; + + void InitTextPrinter(clang::DiagnosticOptions *diags) { + TextPrinter = + std::make_unique(TextStream, diags); + } + + void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, + const clang::Diagnostic &Info) override { + switch (Info.getID()) { + case clang::diag::err_expected: { + for (unsigned Index = 0; Index < Info.getNumArgs(); ++Index) { + if (Info.getArgKind(Index) == clang::DiagnosticsEngine::ak_tokenkind) { + clang::tok::TokenKind Kind = + static_cast(Info.getRawArg(Index)); + if (Kind == clang::tok::r_brace) { + Truncated = true; + } + } + } + break; + } + case clang::diag::err_expected_semi_declaration: + case clang::diag::err_expected_semi_after_stmt: + case clang::diag::err_expected_semi_after_expr: { + Truncated = true; + break; + } + default: + break; + } + TextPrinter->HandleDiagnostic(DiagLevel, Info); + } + + void BeginSourceFile(const clang::LangOptions &LO, + const clang::Preprocessor *PP) override { + TextPrinter->BeginSourceFile(LO, PP); + } + + void EndSourceFile() override { TextPrinter->EndSourceFile(); } + + bool IsCodeTruncated() { return Truncated; } + + void ClearDiagnostics() { + Truncated = false; + TextBuffer.clear(); + } + + void FlushDiagnostics() { + llvm::outs() << TextBuffer; + ClearDiagnostics(); + } + +private: + bool Truncated = false; + std::string TextBuffer; + llvm::raw_string_ostream TextStream; + std::unique_ptr TextPrinter; +}; + static void LLVMErrorHandler(void *UserData, const char *Message, bool GenCrashDiag) { auto &Diags = *static_cast(UserData); @@ -89,8 +164,11 @@ // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It // can replace the boilerplate code for creation of the compiler instance. - auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv)); - + auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create( + ClangArgv, std::make_unique())); + auto &DiagConsumer = + static_cast(CI->getDiagnosticClient()); + DiagConsumer.InitTextPrinter(&CI->getDiagnosticOpts()); // Set an error handler, so that any LLVM backend diagnostics go through our // error handler. llvm::install_fatal_error_handler(LLVMErrorHandler, @@ -105,6 +183,7 @@ llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); } + std::stringstream CodeBuffer; if (OptInputs.empty()) { llvm::LineEditor LE("clang-repl"); // FIXME: Add LE.setListCompleter @@ -112,13 +191,22 @@ if (*Line == R"(%quit)") break; if (*Line == R"(%undo)") { + CodeBuffer.clear(); if (auto Err = Interp->Undo()) llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); continue; } - - if (auto Err = Interp->ParseAndExecute(*Line)) - llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); + CodeBuffer << *Line << "\n"; + if (auto Err = Interp->ParseAndExecute(CodeBuffer.str())) { + if (DiagConsumer.IsCodeTruncated()) { + DiagConsumer.ClearDiagnostics(); + llvm::consumeError(std::move(Err)); + } else { + DiagConsumer.FlushDiagnostics(); + CodeBuffer.str(std::string()); + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: "); + } + } } }