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
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
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.assertEqual(
str(diag.GetValueForKey("message")), "Ran expression as 'C++11'."
)
self.assertEqual(
str(diag.GetValueForKey("rendered")), "Ran expression as 'C++11'."
)
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",
)
87 changes: 87 additions & 0 deletions lldb/test/Shell/Expr/TestExprLanguageNote.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# RUN: split-file %s %t
# RUN: %clang_host -g %t/main.cpp -o %t.out
#
# RUN: %lldb -x -b -o "settings set interpreter.stop-command-source-on-error false" \
# RUN: -s %t/no-target.input 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TARGET
#
# RUN: %lldb %t.out -x -b -o "settings set interpreter.stop-command-source-on-error false" \
# RUN: -s %t/with-target.input 2>&1 | FileCheck %s --check-prefix=CHECK-TARGET

#--- main.cpp

int main() {
int x = 10;
__builtin_debugtrap();
}

#--- with-target.input

expr blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Falling back to default language. Ran expression as 'Objective C++'.

run

expr blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Ran expression as 'C++14'.

expr -l objc -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Expression evaluation in pure Objective-C not supported. Ran expression as 'Objective C++'.

expr -l c -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Expression evaluation in pure C not supported. Ran expression as 'ISO C++'.

expr -l c++14 -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Ran expression as 'C++14'

expr -l c++20 -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Ran expression as 'C++20'

expr -l objective-c++ -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Ran expression as 'Objective C++'

# D uses TypeSystemClang but running expressions in it isn't supported. Test that we warn about this.
expr -l D -- blah

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET: note: Expression evaluation in D not supported. Falling back to default language. Ran expression as 'Objective C++'.

expr -l c++17 -- x = 5

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET-NOT: note:

expr x = 5

# CHECK-TARGET: (lldb) expr
# CHECK-TARGET-NOT: note:

#--- no-target.input

expr blah

# CHECK-NO-TARGET: (lldb) expr
# CHECK-NO-TARGET: note: Falling back to default language. Ran expression as 'Objective C++'.

expr -l c++ -- 1 + 1

# CHECK-NO-TARGET: (lldb) expr
# CHECK-NO-TARGET-NOT: note:

expr 1 + 1

# CHECK-NO-TARGET: (lldb) expr
# CHECK-NO-TARGET-NOT: note: