[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 01:59:10 PDT 2025


https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/161688

>From 8f7c7d59b74119fc16ef2b0e7f7f8c3de925697a 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 8381df60c920ce659f6e135cb47988b9146fda93 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/Language.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/Language.cpp b/lldb/unittests/Target/Language.cpp
index a00fda78d569a..e9745c9315b72 100644
--- a/lldb/unittests/Target/Language.cpp
+++ b/lldb/unittests/Target/Language.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 a84a3db3176833f653b8aeda9a6dc656d3850031 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 07ad8553ddb127940556dcceb39fcb08dd524eae 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 79ed06c7cfb5830f66c45eba3747f0f7844e021f 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 d5457cd11fb1b128d49917702685e378c4bda844 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