Skip to content
Open
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
7 changes: 7 additions & 0 deletions lldb/include/lldb/Target/Language.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,15 @@ class Language : public PluginInterface {
GetLanguageTypeFromString(const char *string) = delete;
static lldb::LanguageType GetLanguageTypeFromString(llvm::StringRef string);

/// Returns the internal LLDB name for the specified language. When presenting
/// the language name to users, use \ref GetDisplayNameForLanguageType
/// instead.
static const char *GetNameForLanguageType(lldb::LanguageType language);

/// Returns a user-friendly name for the specified language.
static llvm::StringRef
GetDisplayNameForLanguageType(lldb::LanguageType language);

static void PrintAllLanguages(Stream &s, const char *prefix,
const char *suffix);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Module.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Host/File.h"
Expand All @@ -96,6 +97,7 @@
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
#include "Plugins/Platform/MacOSX/PlatformDarwin.h"
#include "lldb/Utility/XcodeSDK.h"
#include "lldb/lldb-enumerations.h"

#include <cctype>
#include <memory>
Expand Down Expand Up @@ -570,7 +572,8 @@ static void SetupTargetOpts(CompilerInstance &compiler,

static void SetupLangOpts(CompilerInstance &compiler,
ExecutionContextScope &exe_scope,
const Expression &expr) {
const Expression &expr,
DiagnosticManager &diagnostic_manager) {
Log *log = GetLog(LLDBLog::Expressions);

// If the expression is being evaluated in the context of an existing stack
Expand All @@ -590,6 +593,9 @@ static void SetupLangOpts(CompilerInstance &compiler,
: lldb::eLanguageTypeUnknown),
lldb_private::Language::GetNameForLanguageType(language));

lldb::LanguageType language_for_note = language;
std::string language_fallback_reason;

LangOptions &lang_opts = compiler.getLangOpts();

switch (language) {
Expand All @@ -603,13 +609,21 @@ static void SetupLangOpts(CompilerInstance &compiler,
// family language, because the expression parser uses features of C++ to
// capture values.
lang_opts.CPlusPlus = true;

language_for_note = lldb::eLanguageTypeC_plus_plus;
language_fallback_reason =
"Expression evaluation in pure C not supported. ";
break;
case lldb::eLanguageTypeObjC:
lang_opts.ObjC = true;
// FIXME: the following language option is a temporary workaround,
// to "ask for ObjC, get ObjC++" (see comment above).
lang_opts.CPlusPlus = true;

language_for_note = lldb::eLanguageTypeObjC_plus_plus;
language_fallback_reason =
"Expression evaluation in pure Objective-C not supported. ";

// Clang now sets as default C++14 as the default standard (with
// GNU extensions), so we do the same here to avoid mismatches that
// cause compiler error when evaluating expressions (e.g. nullptr not found
Expand Down Expand Up @@ -650,9 +664,27 @@ static void SetupLangOpts(CompilerInstance &compiler,
lang_opts.CPlusPlus = true;
lang_opts.CPlusPlus11 = true;
compiler.getHeaderSearchOpts().UseLibcxx = true;

language_for_note = lldb::eLanguageTypeObjC_plus_plus;
if (language != language_for_note) {
if (language != lldb::eLanguageTypeUnknown)
language_fallback_reason = llvm::formatv(
"Expression evaluation in {0} not supported. ",
lldb_private::Language::GetDisplayNameForLanguageType(language));

language_fallback_reason +=
llvm::formatv("Falling back to default language. ");
}
break;
}

diagnostic_manager.AddDiagnostic(
llvm::formatv("{0}Ran expression as '{1}'.", language_fallback_reason,
lldb_private::Language::GetDisplayNameForLanguageType(
language_for_note))
.str(),
lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);

lang_opts.Bool = true;
lang_opts.WChar = true;
lang_opts.Blocks = true;
Expand Down Expand Up @@ -736,8 +768,8 @@ static void SetupImportStdModuleLangOpts(CompilerInstance &compiler,

ClangExpressionParser::ClangExpressionParser(
ExecutionContextScope *exe_scope, Expression &expr,
bool generate_debug_info, std::vector<std::string> include_directories,
std::string filename)
bool generate_debug_info, DiagnosticManager &diagnostic_manager,
std::vector<std::string> include_directories, std::string filename)
: ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
m_pp_callbacks(nullptr),
m_include_directories(std::move(include_directories)),
Expand Down Expand Up @@ -801,7 +833,7 @@ ClangExpressionParser::ClangExpressionParser(
}

// 4. Set language options.
SetupLangOpts(*m_compiler, *exe_scope, expr);
SetupLangOpts(*m_compiler, *exe_scope, expr, diagnostic_manager);
auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
if (clang_expr && clang_expr->DidImportCxxModules()) {
LLDB_LOG(log, "Adding lang options for importing C++ modules");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ClangExpressionParser : public ExpressionParser {
/// diagnostics (i.e. errors, warnings or notes from Clang).
ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
bool generate_debug_info,
DiagnosticManager &diagnostic_manager,
std::vector<std::string> include_directories = {},
std::string filename = "<clang expression>");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
if (jit_process_sp) {
const bool generate_debug_info = true;
auto *clang_parser = new ClangExpressionParser(jit_process_sp.get(), *this,
generate_debug_info);
auto *clang_parser = new ClangExpressionParser(
jit_process_sp.get(), *this, generate_debug_info, diagnostic_manager);
num_errors = clang_parser->Parse(diagnostic_manager);
m_parser.reset(clang_parser);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ bool ClangUserExpression::TryParse(

m_parser = std::make_unique<ClangExpressionParser>(
exe_ctx.GetBestExecutionContextScope(), *this, generate_debug_info,
m_include_directories, m_filename);
diagnostic_manager, m_include_directories, m_filename);

unsigned num_errors = m_parser->Parse(diagnostic_manager);

Expand Down Expand Up @@ -818,7 +818,7 @@ bool ClangUserExpression::Complete(ExecutionContext &exe_ctx,
}

ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
false);
false, diagnostic_manager);

// We have to find the source code location where the user text is inside
// the transformed expression code. When creating the transformed text, we
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager,

const bool generate_debug_info = true;
ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
generate_debug_info);
generate_debug_info, diagnostic_manager);

unsigned num_errors = parser.Parse(diagnostic_manager);

Expand Down
30 changes: 24 additions & 6 deletions lldb/source/Target/Language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ const char *Language::GetNameForLanguageType(LanguageType language) {
return language_names[eLanguageTypeUnknown].name;
}

llvm::StringRef Language::GetDisplayNameForLanguageType(LanguageType language) {
return SourceLanguage(language).GetDescription();
}

void Language::PrintSupportedLanguagesForExpressions(Stream &s,
llvm::StringRef prefix,
llvm::StringRef suffix) {
Expand Down Expand Up @@ -544,9 +548,26 @@ Language::Language() = default;
// Destructor
Language::~Language() = default;

static std::optional<llvm::dwarf::SourceLanguage>
ToDwarfSourceLanguage(lldb::LanguageType language_type) {
if (language_type < lldb::eLanguageTypeLastStandardLanguage)
return static_cast<llvm::dwarf::SourceLanguage>(language_type);

switch (language_type) {
case eLanguageTypeMipsAssembler:
return llvm::dwarf::DW_LANG_Mips_Assembler;
default:
return std::nullopt;
}
}

SourceLanguage::SourceLanguage(lldb::LanguageType language_type) {
auto lname =
llvm::dwarf::toDW_LNAME((llvm::dwarf::SourceLanguage)language_type);
std::optional<llvm::dwarf::SourceLanguage> dwarf_lang =
ToDwarfSourceLanguage(language_type);
if (!dwarf_lang)
return;

auto lname = llvm::dwarf::toDW_LNAME(*dwarf_lang);
if (!lname)
return;
name = lname->first;
Expand All @@ -561,11 +582,8 @@ lldb::LanguageType SourceLanguage::AsLanguageType() const {
}

llvm::StringRef SourceLanguage::GetDescription() const {
LanguageType type = AsLanguageType();
if (type)
return Language::GetNameForLanguageType(type);
return llvm::dwarf::LanguageDescription(
(llvm::dwarf::SourceLanguageName)name);
static_cast<llvm::dwarf::SourceLanguageName>(name));
}
bool SourceLanguage::IsC() const { return name == llvm::dwarf::DW_LNAME_C; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,22 @@ def check_error(diags):

details = diags.GetValueForKey("details")

# Detail 1/2: undeclared 'a'
# Detail 1/3: note: requested expression language
diag = details.GetItemAtIndex(0)
self.assertEqual(str(diag.GetValueForKey("severity")), "note")
self.assertIn(
"Ran expression as 'C++", str(diag.GetValueForKey("message"))
)
self.assertIn(
"Ran expression as 'C++", str(diag.GetValueForKey("rendered"))
)
self.assertEqual(str(diag.GetValueForKey("source_location")), "")
self.assertEqual(str(diag.GetValueForKey("file")), "")
self.assertFalse(diag.GetValueForKey("hidden").GetBooleanValue())
self.assertFalse(diag.GetValueForKey("in_user_input").GetBooleanValue())

# Detail 2/3: undeclared 'a'
diag = details.GetItemAtIndex(1)

severity = diag.GetValueForKey("severity")
message = diag.GetValueForKey("message")
Expand All @@ -234,8 +248,8 @@ def check_error(diags):
self.assertFalse(hidden.GetBooleanValue())
self.assertTrue(in_user_input.GetBooleanValue())

# Detail 1/2: undeclared 'b'
diag = details.GetItemAtIndex(1)
# Detail 3/3: undeclared 'b'
diag = details.GetItemAtIndex(2)
message = diag.GetValueForKey("message")
self.assertIn("undeclared identifier 'b'", str(message))

Expand Down
50 changes: 39 additions & 11 deletions lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Test the SBCommandInterpreter APIs."""
"""tESt the SBCommandInterpreter APIs."""

import json
import lldb
Expand Down Expand Up @@ -156,13 +156,15 @@ def test_get_transcript(self):
self.assertEqual(transcript[0]["error"], "")

# (lldb) an-unknown-command
self.assertEqual(transcript[1],
self.assertEqual(
transcript[1],
{
"command": "an-unknown-command",
# Unresolved commands don't have "commandName"/"commandArguments"
"output": "",
"error": "error: 'an-unknown-command' is not a valid command.\n",
})
},
)

# (lldb) br s -f main.c -l <line>
self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line)
Expand All @@ -175,14 +177,17 @@ def test_get_transcript(self):
self.assertEqual(transcript[2]["error"], "")

# (lldb) p a
self.assertEqual(transcript[3],
self.assertEqual(
transcript[3],
{
"command": "p a",
"commandName": "dwim-print",
"commandArguments": "-- a",
"output": "",
"error": "error: <user expression 0>:1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n",
})
"error": "note: Falling back to default language. Ran expression as 'Objective C++'.\n"
"error: <user expression 0>:1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n",
},
)

# (lldb) statistics dump
self.assertEqual(transcript[4]["command"], "statistics dump")
Expand All @@ -203,7 +208,10 @@ def test_save_transcript_setting_default(self):
self.assertTrue(ci, VALID_COMMAND_INTERPRETER)

# The setting's default value should be "false"
self.runCmd("settings show interpreter.save-transcript", "interpreter.save-transcript (boolean) = false\n")
self.runCmd(
"settings show interpreter.save-transcript",
"interpreter.save-transcript (boolean) = false\n",
)

def test_save_transcript_setting_off(self):
ci = self.dbg.GetCommandInterpreter()
Expand Down Expand Up @@ -250,17 +258,37 @@ def test_get_transcript_returns_copy(self):
structured_data_1 = ci.GetTranscript()
self.assertTrue(structured_data_1.IsValid())
self.assertEqual(structured_data_1.GetSize(), 1)
self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
self.assertEqual(
structured_data_1.GetItemAtIndex(0)
.GetValueForKey("command")
.GetStringValue(100),
"version",
)

# Run some more commands and get the transcript as structured data again
self.runCmd("help")
structured_data_2 = ci.GetTranscript()
self.assertTrue(structured_data_2.IsValid())
self.assertEqual(structured_data_2.GetSize(), 2)
self.assertEqual(structured_data_2.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
self.assertEqual(structured_data_2.GetItemAtIndex(1).GetValueForKey("command").GetStringValue(100), "help")
self.assertEqual(
structured_data_2.GetItemAtIndex(0)
.GetValueForKey("command")
.GetStringValue(100),
"version",
)
self.assertEqual(
structured_data_2.GetItemAtIndex(1)
.GetValueForKey("command")
.GetStringValue(100),
"help",
)

# Now, the first structured data should remain unchanged
self.assertTrue(structured_data_1.IsValid())
self.assertEqual(structured_data_1.GetSize(), 1)
self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
self.assertEqual(
structured_data_1.GetItemAtIndex(0)
.GetValueForKey("command")
.GetStringValue(100),
"version",
)
Loading