[Lldb-commits] [lldb] [llvm] [lldb][Expression] Emit a 'Note' diagnostic that indicates the language used for expression evaluation (PR #161688)
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 6 02:13:44 PDT 2025
https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/161688
>From d0501264b050ef8c0736f528599b68972175c2a7 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 6 Oct 2025 09:37:51 +0100
Subject: [PATCH 1/6] [llvm][Dwarf] Add LanguageDescription API that accounts
for version
Currently `llvm::dwarf::LanguageDescription` returns a stringified
`DW_LNAME`. It would be useful to have an API that returns the language
name for a particular `DW_LNAME_`/version pair. LLDB's use case is that
it wants to display a human readable description of the language we got
from debug-info in diagnostics. We could maintain a side-table in LLDB
but though this might generally be useful to live next to the
`LanguageDescription` API.
(cherry picked from commit 544d86112320b1512e3ce3aaa11d177f47d5e206)
---
llvm/include/llvm/BinaryFormat/Dwarf.h | 7 ++
llvm/lib/BinaryFormat/Dwarf.cpp | 111 +++++++++++++++++++++++++
2 files changed, 118 insertions(+)
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h
index 2c5012510a5c3..624e5f63f2270 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.h
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.h
@@ -500,8 +500,15 @@ toDW_LNAME(SourceLanguage language) {
return {};
}
+/// Returns a version-independent language name.
LLVM_ABI llvm::StringRef LanguageDescription(SourceLanguageName name);
+/// Returns a language name corresponding to the specified version.
+/// If the version is not recognized for the specified language, returns
+/// the version-independent name.
+LLVM_ABI llvm::StringRef LanguageDescription(SourceLanguageName Name,
+ uint32_t Version);
+
inline bool isCPlusPlus(SourceLanguage S) {
bool result = false;
// Deliberately enumerate all the language options so we get a warning when
diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp
index 8b24044e19e50..0202dffc3d6b2 100644
--- a/llvm/lib/BinaryFormat/Dwarf.cpp
+++ b/llvm/lib/BinaryFormat/Dwarf.cpp
@@ -472,6 +472,117 @@ StringRef llvm::dwarf::LanguageDescription(dwarf::SourceLanguageName lname) {
return "Unknown";
}
+StringRef llvm::dwarf::LanguageDescription(dwarf::SourceLanguageName Name,
+ uint32_t Version) {
+ switch (Name) {
+ // YYYY
+ case DW_LNAME_Ada: {
+ if (Version <= 1983)
+ return "Ada 83";
+ if (Version <= 1995)
+ return "Ada 95";
+ if (Version <= 2005)
+ return "Ada 2005";
+ if (Version <= 2012)
+ return "Ada 2012";
+ } break;
+
+ case DW_LNAME_Cobol: {
+ if (Version <= 1974)
+ return "COBOL-74";
+ if (Version <= 1985)
+ return "COBOL-85";
+ } break;
+
+ case DW_LNAME_Fortran: {
+ if (Version <= 1977)
+ return "FORTRAN 77";
+ if (Version <= 1990)
+ return "FORTRAN 90";
+ if (Version <= 1995)
+ return "Fortran 95";
+ if (Version <= 2003)
+ return "Fortran 2003";
+ if (Version <= 2008)
+ return "Fortran 2008";
+ if (Version <= 2018)
+ return "Fortran 2018";
+ } break;
+
+ // YYYYMM
+ case DW_LNAME_C: {
+ if (Version == 0)
+ break;
+ if (Version <= 198912)
+ return "C89";
+ if (Version <= 199901)
+ return "C99";
+ if (Version <= 201112)
+ return "C11";
+ if (Version <= 201710)
+ return "C17";
+ } break;
+
+ case DW_LNAME_C_plus_plus: {
+ if (Version == 0)
+ break;
+ if (Version <= 199711)
+ return "C++98";
+ if (Version <= 200310)
+ return "C++03";
+ if (Version <= 201103)
+ return "C++11";
+ if (Version <= 201402)
+ return "C++14";
+ if (Version <= 201703)
+ return "C++17";
+ if (Version <= 202002)
+ return "C++20";
+ } break;
+
+ case DW_LNAME_ObjC_plus_plus:
+ case DW_LNAME_ObjC:
+ case DW_LNAME_Move:
+ case DW_LNAME_SYCL:
+ case DW_LNAME_BLISS:
+ case DW_LNAME_Crystal:
+ case DW_LNAME_D:
+ case DW_LNAME_Dylan:
+ case DW_LNAME_Go:
+ case DW_LNAME_Haskell:
+ case DW_LNAME_HLSL:
+ case DW_LNAME_Java:
+ case DW_LNAME_Julia:
+ case DW_LNAME_Kotlin:
+ case DW_LNAME_Modula2:
+ case DW_LNAME_Modula3:
+ case DW_LNAME_OCaml:
+ case DW_LNAME_OpenCL_C:
+ case DW_LNAME_Pascal:
+ case DW_LNAME_PLI:
+ case DW_LNAME_Python:
+ case DW_LNAME_RenderScript:
+ case DW_LNAME_Rust:
+ case DW_LNAME_Swift:
+ case DW_LNAME_UPC:
+ case DW_LNAME_Zig:
+ case DW_LNAME_Assembly:
+ case DW_LNAME_C_sharp:
+ case DW_LNAME_Mojo:
+ case DW_LNAME_GLSL:
+ case DW_LNAME_GLSL_ES:
+ case DW_LNAME_OpenCL_CPP:
+ case DW_LNAME_CPP_for_OpenCL:
+ case DW_LNAME_Ruby:
+ case DW_LNAME_Hylo:
+ case DW_LNAME_Metal:
+ break;
+ }
+
+ // Fallback to un-versioned name.
+ return LanguageDescription(Name);
+}
+
StringRef llvm::dwarf::CaseString(unsigned Case) {
switch (Case) {
case DW_ID_case_sensitive:
>From 36bbdf0f7684f787207b5d71a88c0797f70e45d5 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 6 Oct 2025 09:48:28 +0100
Subject: [PATCH 2/6] [lldb][Language] Make SourceLanguage::GetDescription for
language version
This makes sure we also include the version number in the description.
For `C++17`, this would, e.g., now return `"C++17"` instead of `"ISO C++"`.
---
lldb/source/Target/Language.cpp | 2 +-
lldb/unittests/Target/LanguageTest.cpp | 9 +++++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp
index 395718ecbe292..9119f8989ed98 100644
--- a/lldb/source/Target/Language.cpp
+++ b/lldb/source/Target/Language.cpp
@@ -582,7 +582,7 @@ lldb::LanguageType SourceLanguage::AsLanguageType() const {
llvm::StringRef SourceLanguage::GetDescription() const {
return llvm::dwarf::LanguageDescription(
- static_cast<llvm::dwarf::SourceLanguageName>(name));
+ static_cast<llvm::dwarf::SourceLanguageName>(name), version);
}
bool SourceLanguage::IsC() const { return name == llvm::dwarf::DW_LNAME_C; }
diff --git a/lldb/unittests/Target/LanguageTest.cpp b/lldb/unittests/Target/LanguageTest.cpp
index a00fda78d569a..e9745c9315b72 100644
--- a/lldb/unittests/Target/LanguageTest.cpp
+++ b/lldb/unittests/Target/LanguageTest.cpp
@@ -39,9 +39,14 @@ TEST_F(LanguageTest, SourceLanguage_GetDescription) {
EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus).GetDescription(),
"ISO C++");
EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_17).GetDescription(),
- "ISO C++");
+ "C++17");
EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_20).GetDescription(),
- "ISO C++");
+ "C++20");
+
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC).GetDescription(),
+ "C (K&R and ISO)");
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC89).GetDescription(),
+ "C89");
EXPECT_EQ(SourceLanguage(eLanguageTypeObjC).GetDescription(), "Objective C");
EXPECT_EQ(SourceLanguage(eLanguageTypeMipsAssembler).GetDescription(),
>From 63395ec4fa8cfaf6320d514c35fd9da7b2eeabd9 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 2 Oct 2025 16:24:12 +0100
Subject: [PATCH 3/6] [lldb][Expression] Emit a 'Note' diagnistc that indicates
the language used for expression evaluation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Since it's a 'Note' diagnostic it would only show up when expression
evaluation actually failed. This helps with expression evaluation
failure reports in mixed language environments where it's not quite clear
what language the expression ran as. It may also reduce confusion around
why the expression evaluator ran an expression in a language it wasn't
asked to run (a softer alternative to what I attempted in https://github.com/llvm/llvm-project/pull/156648).
Here are some example outputs:
```
(lldb) expr -l c -- blah
˄
╰─ error: use of undeclared identifier 'blah'
note: Requested expression evaluation as 'c' but fell back to 'c++'.
(lldb) expr -l c++ -- blah
˄
╰─ error: use of undeclared identifier 'blah'
note: Requested expression evaluation as c++
(lldb) expr -l objc -- blah
˄
╰─ error: use of undeclared identifier 'blah'
note: Requested expression evaluation as 'objective-c' but fell back to 'objective-c++'.
(lldb) expr -l rust -- blah
˄
╰─ error: use of undeclared identifier 'blah'
note: Requested expression evaluation as rust
```
I didn't put the diagnostic on the same line as the inline diagnostic
for now because of implementation convenience, but if reviewers deem
that a blocker I can take a stab at that again.
Also, other language plugins (namely Swift), won't immediately benefit
from this and will have to emit their own diagnistc. I played around
with having a virtual API on `UserExpression` or `ExpressionParser` that
will be called consistently, but by the time we're about to parse the
expression we are already several frames deep into the plugin. Before
(and at the beginning of) the generic `UserExpression::Parse` call we
don't have enough information to notify which language we're going to
parse in (at least for the C++ plugin).
rdar://160297649
---
.../Clang/ClangExpressionParser.cpp | 29 ++++++++++++++++---
.../Clang/ClangExpressionParser.h | 1 +
.../Clang/ClangFunctionCaller.cpp | 4 +--
.../Clang/ClangUserExpression.cpp | 4 +--
.../Clang/ClangUtilityFunction.cpp | 2 +-
5 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 3c49c911108a3..b3aebfcc77039 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"
@@ -527,7 +528,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 +549,8 @@ static void SetupLangOpts(CompilerInstance &compiler,
: lldb::eLanguageTypeUnknown),
lldb_private::Language::GetNameForLanguageType(language));
+ lldb::LanguageType language_for_note = language;
+
LangOptions &lang_opts = compiler.getLangOpts();
switch (language) {
@@ -560,12 +564,14 @@ 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;
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;
// Clang now sets as default C++14 as the default standard (with
// GNU extensions), so we do the same here to avoid mismatches that
@@ -610,6 +616,21 @@ static void SetupLangOpts(CompilerInstance &compiler,
break;
}
+ if (language_for_note != language)
+ diagnostic_manager.AddDiagnostic(
+ llvm::formatv(
+ "Requested expression evaluation as '{0}' but fell back to '{1}'.",
+ lldb_private::Language::GetNameForLanguageType(language),
+ lldb_private::Language::GetNameForLanguageType(language_for_note))
+ .str(),
+ lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
+ else
+ diagnostic_manager.AddDiagnostic(
+ llvm::formatv("Requested expression evaluation as '{0}'",
+ lldb_private::Language::GetNameForLanguageType(language))
+ .str(),
+ lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
+
lang_opts.Bool = true;
lang_opts.WChar = true;
lang_opts.Blocks = true;
@@ -687,8 +708,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)),
@@ -754,7 +775,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");
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<std::string> include_directories = {},
std::string filename = "<clang expression>");
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<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);
@@ -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);
>From e9df9830dd1bcbe944c342552b58a1667b02b0e1 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 3 Oct 2025 09:29:17 +0100
Subject: [PATCH 4/6] fixup! reword diagnostic; use
GetDisplayNameForLanguageType
---
.../ExpressionParser/Clang/ClangExpressionParser.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index b3aebfcc77039..a94bf475e0ece 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -619,15 +619,15 @@ static void SetupLangOpts(CompilerInstance &compiler,
if (language_for_note != language)
diagnostic_manager.AddDiagnostic(
llvm::formatv(
- "Requested expression evaluation as '{0}' but fell back to '{1}'.",
- lldb_private::Language::GetNameForLanguageType(language),
- lldb_private::Language::GetNameForLanguageType(language_for_note))
+ "Requested expression evaluation in '{0}' is not supported. Used '{1}' instead.",
+ lldb_private::Language::GetDisplayNameForLanguageType(language),
+ lldb_private::Language::GetDisplayNameForLanguageType(language_for_note))
.str(),
lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
else
diagnostic_manager.AddDiagnostic(
- llvm::formatv("Requested expression evaluation as '{0}'",
- lldb_private::Language::GetNameForLanguageType(language))
+ llvm::formatv("Requested expression evaluation as '{0}'.",
+ lldb_private::Language::GetDisplayNameForLanguageType(language))
.str(),
lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
>From e265c63328c4dc393faf3a51eb3d3eb3cee1f111 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Fri, 3 Oct 2025 09:30:05 +0100
Subject: [PATCH 5/6] fixup! clang-format
---
.../ExpressionParser/Clang/ClangExpressionParser.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index a94bf475e0ece..347dfd166be95 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -619,15 +619,18 @@ static void SetupLangOpts(CompilerInstance &compiler,
if (language_for_note != language)
diagnostic_manager.AddDiagnostic(
llvm::formatv(
- "Requested expression evaluation in '{0}' is not supported. Used '{1}' instead.",
+ "Requested expression evaluation in '{0}' is not supported. Used "
+ "'{1}' instead.",
lldb_private::Language::GetDisplayNameForLanguageType(language),
- lldb_private::Language::GetDisplayNameForLanguageType(language_for_note))
+ lldb_private::Language::GetDisplayNameForLanguageType(
+ language_for_note))
.str(),
lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
else
diagnostic_manager.AddDiagnostic(
- llvm::formatv("Requested expression evaluation as '{0}'.",
- lldb_private::Language::GetDisplayNameForLanguageType(language))
+ llvm::formatv(
+ "Requested expression evaluation as '{0}'.",
+ lldb_private::Language::GetDisplayNameForLanguageType(language))
.str(),
lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
>From d62e766853d41a613cbde0c3f47067a879df2a40 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Sat, 4 Oct 2025 00:42:34 +0100
Subject: [PATCH 6/6] fixup! fix tests+reword diagnostics
---
.../Clang/ClangExpressionParser.cpp | 34 ++++++-------
.../diagnostics/TestExprDiagnostics.py | 10 ++--
.../interpreter/TestCommandInterpreterAPI.py | 48 +++++++++++++++----
3 files changed, 62 insertions(+), 30 deletions(-)
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 347dfd166be95..7eb13dc8e9dd5 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -97,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>
@@ -550,6 +551,7 @@ static void SetupLangOpts(CompilerInstance &compiler,
lldb_private::Language::GetNameForLanguageType(language));
lldb::LanguageType language_for_note = language;
+ llvm::StringRef language_fallback_reason;
LangOptions &lang_opts = compiler.getLangOpts();
@@ -564,14 +566,20 @@ 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
@@ -613,26 +621,18 @@ static void SetupLangOpts(CompilerInstance &compiler,
lang_opts.CPlusPlus = true;
lang_opts.CPlusPlus11 = true;
compiler.getHeaderSearchOpts().UseLibcxx = true;
+
+ language_for_note = lldb::eLanguageTypeObjC_plus_plus;
+ language_fallback_reason = "Using default language. ";
break;
}
- if (language_for_note != language)
- diagnostic_manager.AddDiagnostic(
- llvm::formatv(
- "Requested expression evaluation in '{0}' is not supported. Used "
- "'{1}' instead.",
- lldb_private::Language::GetDisplayNameForLanguageType(language),
- lldb_private::Language::GetDisplayNameForLanguageType(
- language_for_note))
- .str(),
- lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
- else
- diagnostic_manager.AddDiagnostic(
- llvm::formatv(
- "Requested expression evaluation as '{0}'.",
- lldb_private::Language::GetDisplayNameForLanguageType(language))
- .str(),
- lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
+ 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;
diff --git a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
index 0cc505aedc4be..5150ba6a630ad 100644
--- a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
+++ b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
@@ -215,8 +215,12 @@ 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")
+
+ # Detail 2/3: undeclared 'a'
+ diag = details.GetItemAtIndex(1)
severity = diag.GetValueForKey("severity")
message = diag.GetValueForKey("message")
@@ -234,8 +238,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..74ae46b50774d 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -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)
@@ -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: Using 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")
@@ -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",
+ )
More information about the lldb-commits
mailing list