diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 3c49c911108a3..6b121c934dfbb 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -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" @@ -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 #include @@ -527,7 +529,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 @@ -547,6 +550,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) { @@ -560,6 +566,10 @@ 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; @@ -567,6 +577,10 @@ static void SetupLangOpts(CompilerInstance &compiler, // 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 @@ -607,9 +621,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; @@ -687,8 +719,8 @@ static void SetupImportStdModuleLangOpts(CompilerInstance &compiler, ClangExpressionParser::ClangExpressionParser( ExecutionContextScope *exe_scope, Expression &expr, - bool generate_debug_info, std::vector include_directories, - std::string filename) + bool generate_debug_info, DiagnosticManager &diagnostic_manager, + std::vector 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)), @@ -754,7 +786,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(&m_expr); if (clang_expr && clang_expr->DidImportCxxModules()) { LLDB_LOG(log, "Adding lang options for importing C++ modules"); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h index 93e0b007dbcc8..734ad51c9646e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h @@ -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 include_directories = {}, std::string filename = ""); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp index e4a094f3aa512..d2db319afb7a0 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp @@ -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 { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index 6b743e29e21f6..e8d5ec3c7fd96 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -574,7 +574,7 @@ bool ClangUserExpression::TryParse( m_parser = std::make_unique( 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); @@ -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 diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp index 1f44200c4cff8..e6983066a12fa 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -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); diff --git a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py index 0cc505aedc4be..ec208f2c32503 100644 --- a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py +++ b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py @@ -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") @@ -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)) diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py index 1029bdc3096d0..01ed11a5a1121 100644 --- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py +++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py @@ -1,4 +1,4 @@ -"""Test the SBCommandInterpreter APIs.""" +"""tESt the SBCommandInterpreter APIs.""" import json import lldb @@ -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 self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line) @@ -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: :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: :1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n", + }, + ) # (lldb) statistics dump self.assertEqual(transcript[4]["command"], "statistics dump") @@ -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() @@ -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", + ) diff --git a/lldb/test/Shell/Expr/TestExprLanguageNote.test b/lldb/test/Shell/Expr/TestExprLanguageNote.test new file mode 100644 index 0000000000000..f3dc5928fcca9 --- /dev/null +++ b/lldb/test/Shell/Expr/TestExprLanguageNote.test @@ -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: