Skip to content

Backport [lldb] Print full Clang diagnostics when the ClangModulesDeclVendor fails to compile a module #1253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Parse/Parser.h"
Expand Down Expand Up @@ -54,10 +55,21 @@ class StoringDiagnosticConsumer : public clang::DiagnosticConsumer {

void DumpDiagnostics(Stream &error_stream);

void BeginSourceFile(const clang::LangOptions &LangOpts,
const clang::Preprocessor *PP = nullptr) override;
void EndSourceFile() override;

private:
typedef std::pair<clang::DiagnosticsEngine::Level, std::string>
IDAndDiagnostic;
std::vector<IDAndDiagnostic> m_diagnostics;
/// The DiagnosticPrinter used for creating the full diagnostic messages
/// that are stored in m_diagnostics.
std::shared_ptr<clang::TextDiagnosticPrinter> m_diag_printer;
/// Output stream of m_diag_printer.
std::shared_ptr<llvm::raw_string_ostream> m_os;
/// Output string filled by m_os. Will be reused for different diagnostics.
std::string m_output;
Log *m_log;
};

Expand Down Expand Up @@ -116,17 +128,21 @@ class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor {

StoringDiagnosticConsumer::StoringDiagnosticConsumer() {
m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS);

clang::DiagnosticOptions *m_options = new clang::DiagnosticOptions();
m_os.reset(new llvm::raw_string_ostream(m_output));
m_diag_printer.reset(new clang::TextDiagnosticPrinter(*m_os, m_options));
}

void StoringDiagnosticConsumer::HandleDiagnostic(
clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) {
llvm::SmallVector<char, 256> diagnostic_string;
// Print the diagnostic to m_output.
m_output.clear();
m_diag_printer->HandleDiagnostic(DiagLevel, info);
m_os->flush();

info.FormatDiagnostic(diagnostic_string);

m_diagnostics.push_back(
IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(),
diagnostic_string.size())));
// Store the diagnostic for later.
m_diagnostics.push_back(IDAndDiagnostic(DiagLevel, m_output));
}

void StoringDiagnosticConsumer::ClearDiagnostics() { m_diagnostics.clear(); }
Expand All @@ -144,6 +160,15 @@ void StoringDiagnosticConsumer::DumpDiagnostics(Stream &error_stream) {
}
}

void StoringDiagnosticConsumer::BeginSourceFile(
const clang::LangOptions &LangOpts, const clang::Preprocessor *PP) {
m_diag_printer->BeginSourceFile(LangOpts, PP);
}

void StoringDiagnosticConsumer::EndSourceFile() {
m_diag_printer->EndSourceFile();
}

ClangModulesDeclVendor::ClangModulesDeclVendor()
: ClangDeclVendor(eClangModuleDeclVendor) {}

Expand Down
69 changes: 43 additions & 26 deletions lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,37 +347,54 @@ bool ClangUserExpression::SetupPersistentState(DiagnosticManager &diagnostic_man
return true;
}

static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target) {
if (ClangModulesDeclVendor *decl_vendor =
target->GetClangModulesDeclVendor()) {
const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
llvm::cast<ClangPersistentVariables>(
target->GetPersistentExpressionStateForLanguage(
lldb::eLanguageTypeC))
->GetHandLoadedClangModules();
ClangModulesDeclVendor::ModuleVector modules_for_macros;
static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target,
DiagnosticManager &diagnostic_manager) {
ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor();
if (!decl_vendor)
return;

for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
modules_for_macros.push_back(module);
}
if (!target->GetEnableAutoImportClangModules())
return;

if (target->GetEnableAutoImportClangModules()) {
if (StackFrame *frame = exe_ctx.GetFramePtr()) {
if (Block *block = frame->GetFrameBlock()) {
SymbolContext sc;
auto *persistent_state = llvm::cast<ClangPersistentVariables>(
target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC));
if (!persistent_state)
return;

block->CalculateSymbolContext(&sc);
StackFrame *frame = exe_ctx.GetFramePtr();
if (!frame)
return;

if (sc.comp_unit) {
StreamString error_stream;
Block *block = frame->GetFrameBlock();
if (!block)
return;
SymbolContext sc;

decl_vendor->AddModulesForCompileUnit(
*sc.comp_unit, modules_for_macros, error_stream);
}
}
}
}
block->CalculateSymbolContext(&sc);

if (!sc.comp_unit)
return;
StreamString error_stream;

ClangModulesDeclVendor::ModuleVector modules_for_macros =
persistent_state->GetHandLoadedClangModules();
if (decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros,
error_stream))
return;

// Failed to load some modules, so emit the error stream as a diagnostic.
if (!error_stream.Empty()) {
// The error stream already contains several Clang diagnostics that might
// be either errors or warnings, so just print them all as one remark
// diagnostic to prevent that the message starts with "error: error:".
diagnostic_manager.PutString(eDiagnosticSeverityRemark,
error_stream.GetString());
return;
}

diagnostic_manager.PutString(eDiagnosticSeverityError,
"Unknown error while loading modules needed for "
"current compilation unit.");
}

void ClangUserExpression::UpdateLanguageForExpr() {
Expand Down Expand Up @@ -529,7 +546,7 @@ bool ClangUserExpression::PrepareForParsing(

ApplyObjcCastHack(m_expr_text);

SetupDeclVendor(exe_ctx, m_target);
SetupDeclVendor(exe_ctx, m_target, diagnostic_manager);

CppModuleConfiguration module_config = GetModuleConfig(m_language, exe_ctx);
llvm::ArrayRef<std::string> imported_modules =
Expand Down
5 changes: 5 additions & 0 deletions lldb/test/API/lang/objc/modules-compile-error/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
OBJC_SOURCES := main.m

CFLAGS_EXTRAS = $(MANDATORY_MODULE_BUILD_CFLAGS) -I$(BUILDDIR) -DONLY_CLANG=1

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil

class TestCase(TestBase):

mydir = TestBase.compute_mydir(__file__)

@skipUnlessDarwin
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.m"))

# Try importing our custom module. This will fail as LLDB won't define
# the CLANG_ONLY define when it compiles the module for the expression
# evaluator.
# Check that the error message shows file/line/column, prints the relevant
# line from the source code and mentions the module that failed to build.
self.expect("expr @import LLDBTestModule", error=True,
substrs=["module.h:4:1: error: unknown type name 'syntax_error_for_lldb_to_find'",
"syntax_error_for_lldb_to_find // comment that tests source printing",
"could not build module 'LLDBTestModule'"])
5 changes: 5 additions & 0 deletions lldb/test/API/lang/objc/modules-compile-error/main.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import LLDBTestModule;

int main() {
return foo(); // break here
}
5 changes: 5 additions & 0 deletions lldb/test/API/lang/objc/modules-compile-error/module.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
int foo() { return 123; }

#ifndef ONLY_CLANG
syntax_error_for_lldb_to_find // comment that tests source printing
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module LLDBTestModule { header "module.h" export * }