Skip to content

[lldb] Add a hint for potential missing module imports in the express… #9560

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
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
2 changes: 2 additions & 0 deletions lldb/packages/Python/lldbsuite/test/make/Swift.rules
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,11 @@ ifneq "$(FRAMEWORK)" ""
mkdir -p $(FRAMEWORK).framework/Versions/A/Headers
mkdir -p $(FRAMEWORK).framework/Versions/A/Modules
mkdir -p $(FRAMEWORK).framework/Versions/A/Resources
ifeq "$(DYLIB_HIDE_SWIFTMODULE)" ""
ifneq "$(MODULENAME)" ""
mkdir -p $(FRAMEWORK).framework/Versions/A/Modules/$(MODULENAME).swiftmodule
cp -r $(MODULENAME).swiftmodule $(FRAMEWORK).framework/Versions/A/Modules/$(MODULENAME).swiftmodule/$(ARCH).swiftmodule
endif
endif
(cd $(FRAMEWORK).framework/Versions; ln -sf A Current)
(cd $(FRAMEWORK).framework/; ln -sf Versions/A/Headers Headers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,26 @@ RedirectCallFromSinkToTrampolineFunction(llvm::Module &module,
return true;
}

/// Add additional context that is otherwise hard to convey.
static void AnnotateDiagnostics(DiagnosticManager &diagnostic_manager) {
bool append_missing_library_note = false;
for (auto &diag : diagnostic_manager.Diagnostics()) {
if (!diag || diag->GetSeverity() != eSeverityError)
continue;
const auto &detail = diag->GetDetail();
if (StringRef(detail.message).contains("has no member")) {
append_missing_library_note = true;
break;
}
}
if (append_missing_library_note)
diagnostic_manager.AddDiagnostic(
"If the member name is spelled correctly, this error could mean that "
"it depends on a (private) type from a module that has not been "
"imported into the current context.",
eSeverityInfo, eDiagnosticOriginLLDB);
}

SwiftExpressionParser::ParseResult
SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
uint32_t first_line, uint32_t last_line) {
Expand All @@ -1675,6 +1695,7 @@ SwiftExpressionParser::Parse(DiagnosticManager &diagnostic_manager,
"error expected");
expr_diagnostics->PrintDiagnostics(diagnostic_manager, buffer_id,
first_line, last_line);
AnnotateDiagnostics(diagnostic_manager);
};

// In the case of playgrounds, we turn all rewriting functionality off.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,14 @@ class SwiftLanguageRuntimeStub {
llvm::Expected<uint32_t> GetNumChildren(CompilerType type,
ExecutionContextScope *exe_scopej) {
STUB_LOG();
return 0;
return llvm::createStringError("runtime not loaded");
}

std::optional<std::string> GetEnumCaseName(CompilerType type,
llvm::Expected<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx) {
STUB_LOG();
return {};
return llvm::createStringError("runtime not loaded");
}

std::pair<SwiftLanguageRuntime::LookupResult, std::optional<size_t>>
Expand Down Expand Up @@ -2417,7 +2417,7 @@ SwiftLanguageRuntime::GetNumChildren(CompilerType type,
FORWARD(GetNumChildren, type, exe_scope);
}

std::optional<std::string> SwiftLanguageRuntime::GetEnumCaseName(
llvm::Expected<std::string> SwiftLanguageRuntime::GetEnumCaseName(
CompilerType type, const DataExtractor &data, ExecutionContext *exe_ctx) {
FORWARD(GetEnumCaseName, type, data, exe_ctx);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ class SwiftLanguageRuntime : public LanguageRuntime {

/// Determine the enum case name for the \p data value of the enum \p type.
/// This is performed using Swift reflection.
std::optional<std::string> GetEnumCaseName(CompilerType type,
llvm::Expected<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -952,15 +952,20 @@ findFieldWithName(const std::vector<swift::reflection::FieldInfo> &fields,
return {SwiftLanguageRuntime::eFound, child_indexes.size()};
}

std::optional<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(
llvm::Expected<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(
CompilerType type, const DataExtractor &data, ExecutionContext *exe_ctx) {
using namespace swift::reflection;
using namespace swift::remote;
auto *ti = GetSwiftRuntimeTypeInfo(type, exe_ctx->GetFramePtr());
if (!ti)
return {};
return llvm::createStringError("could not get runtime type info for " +
type.GetMangledTypeName().GetStringRef());

// FIXME: Not reported as an error. There seems to be an odd
// compiler optimization happening with single-case payload carrying
// enums, which report their type as the inner type.
if (ti->getKind() != TypeInfoKind::Enum)
return {};
return "";

auto *eti = llvm::cast<EnumTypeInfo>(ti);
PushLocalBuffer((int64_t)data.GetDataStart(), data.GetByteSize());
Expand All @@ -972,7 +977,7 @@ std::optional<std::string> SwiftLanguageRuntimeImpl::GetEnumCaseName(

// TODO: uncomment this after fixing projection for every type: rdar://138424904
// LogUnimplementedTypeKind(__FUNCTION__, type);
return {};
return llvm::createStringError("unimplemented enum kind");
}

std::pair<SwiftLanguageRuntime::LookupResult, std::optional<size_t>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ class SwiftLanguageRuntimeImpl {
std::optional<unsigned> GetNumFields(CompilerType type,
ExecutionContext *exe_ctx);

std::optional<std::string> GetEnumCaseName(CompilerType type,
llvm::Expected<std::string> GetEnumCaseName(CompilerType type,
const DataExtractor &data,
ExecutionContext *exe_ctx);

Expand Down
25 changes: 16 additions & 9 deletions lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "llvm/ADT/ScopeExit.h"

#include <algorithm>
#include <cerrno>
#include <sstream>
#include <type_traits>

Expand Down Expand Up @@ -4412,30 +4413,36 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue(
}
case Node::Kind::Enum:
case Node::Kind::BoundGenericEnum: {
std::string error;
if (exe_scope)
if (auto runtime =
SwiftLanguageRuntime::Get(exe_scope->CalculateProcess())) {
ExecutionContext exe_ctx;
exe_scope->CalculateExecutionContext(exe_ctx);
if (auto case_name = runtime->GetEnumCaseName(
{weak_from_this(), type}, data, &exe_ctx)) {
auto case_name = runtime->GetEnumCaseName({weak_from_this(), type},
data, &exe_ctx);
if (case_name && !case_name->empty()) {
s.PutCString(*case_name);
return true;
}
if (!case_name)
error = toString(case_name.takeError());
}

// No result available from the runtime, fallback to the AST. This occurs
// for some Clang imported enums
// for some Clang imported enums.
if (auto *swift_ast_context =
GetSwiftASTContextFromExecutionScope(exe_scope)) {
ExecutionContext exe_ctx;
exe_scope->CalculateExecutionContext(exe_ctx);
return swift_ast_context->DumpTypeValue(
ReconstructType(type, &exe_ctx), s, format, data, data_offset,
data_byte_size, bitfield_bit_size, bitfield_bit_offset, exe_scope,
is_base_class);
if (swift_ast_context->DumpTypeValue(
ReconstructType(type, &exe_ctx), s, format, data, data_offset,
data_byte_size, bitfield_bit_size, bitfield_bit_offset,
exe_scope, is_base_class))
return true;
}
return {};
s << error;
return false;
}
case Node::Kind::TypeAlias:
case Node::Kind::BoundGenericTypeAlias: {
Expand All @@ -4449,7 +4456,7 @@ bool TypeSystemSwiftTypeRef::DumpTypeValue(
ReconstructType(type, exe_scope), s, format, data, data_offset,
data_byte_size, bitfield_bit_size, bitfield_bit_offset, exe_scope,
is_base_class);
return {};
return false;
}
default:
assert(false && "Unhandled node kind");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
private enum E {
case WithString(String)
case OtherCase
}

public struct S {
private var e : E { get { return .WithString("hidden") } }
}

public func getS() -> S { return S() }
14 changes: 14 additions & 0 deletions lldb/test/API/lang/swift/expression/error_missing_type/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -I.
LD_EXTRAS = -lLibrary -L$(BUILDDIR)
all: libLibrary.dylib a.out

lib%.dylib: %.swift
$(MAKE) MAKE_DSYM=NO DYLIB_ONLY=YES \
DYLIB_HIDE_SWIFTMODULE=YES \
DYLIB_NAME=$(shell basename $< .swift) \
DYLIB_SWIFT_SOURCES=$(shell basename $<) \
VPATH=$(SRCDIR) -I $(SRCDIR) -f $(MAKEFILE_RULES) all
rm -f Library.private.swiftinterface

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

class TestSwiftExpressionErrorMissingType(TestBase):
NO_DEBUG_INFO_TESTCASE = True

@swiftTest
def test(self):
"""Test an extra hint inserted by LLDB for missing module imports"""
self.build()
os.remove(self.getBuildArtifact("Library.swiftmodule"))
lldbutil.run_to_source_breakpoint(self, 'break here',
lldb.SBFileSpec('main.swift'),
extra_images=['Library'])

self.expect('expression s.e', error=True,
substrs=['spelled', 'correctly', 'module', 'import'])
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Library
let s = getS()
print("break here")
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil

class TestSwiftExpressionErrorReportingy(TestBase):
class TestSwiftExpressionErrorReporting(TestBase):
NO_DEBUG_INFO_TESTCASE = True

@swiftTest
Expand Down