diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 87da1a84fe2eb..062c584d5ee2e 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -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); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 2af4544f498cf..8f88e4a2f1726 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 @@ -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 @@ -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) { @@ -603,6 +609,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; @@ -610,6 +620,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 @@ -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; @@ -736,8 +768,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)), @@ -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(&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/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 7911f9481030f..5d359b5ef01d1 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -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) { @@ -544,9 +548,26 @@ Language::Language() = default; // Destructor Language::~Language() = default; +static std::optional +ToDwarfSourceLanguage(lldb::LanguageType language_type) { + if (language_type < lldb::eLanguageTypeLastStandardLanguage) + return static_cast(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 dwarf_lang = + ToDwarfSourceLanguage(language_type); + if (!dwarf_lang) + return; + + auto lname = llvm::dwarf::toDW_LNAME(*dwarf_lang); if (!lname) return; name = lname->first; @@ -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(name)); } bool SourceLanguage::IsC() const { return name == llvm::dwarf::DW_LNAME_C; } 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: diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt index 3169339ec699f..0c79675a3d890 100644 --- a/lldb/unittests/Target/CMakeLists.txt +++ b/lldb/unittests/Target/CMakeLists.txt @@ -2,6 +2,7 @@ add_lldb_unittest(TargetTests ABITest.cpp DynamicRegisterInfoTest.cpp ExecutionContextTest.cpp + Language.cpp LocateModuleCallbackTest.cpp MemoryRegionInfoTest.cpp MemoryTest.cpp diff --git a/lldb/unittests/Target/Language.cpp b/lldb/unittests/Target/Language.cpp new file mode 100644 index 0000000000000..a00fda78d569a --- /dev/null +++ b/lldb/unittests/Target/Language.cpp @@ -0,0 +1,69 @@ +//===-- LanguageTest.cpp --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/Language.h" +#include "lldb/lldb-enumerations.h" +#include "gtest/gtest.h" + +using namespace lldb_private; +using namespace lldb; + +namespace { +class LanguageTest : public ::testing::Test {}; +} // namespace + +TEST_F(LanguageTest, SourceLanguage_GetDescription) { + for (uint32_t i = 1; i < lldb::eNumLanguageTypes; ++i) { + // 0x29 is unassigned + if (i == 0x29) + continue; + + auto lang_type = static_cast(i); + if (lang_type == lldb::eLanguageTypeLastStandardLanguage) + continue; + + SourceLanguage lang(lang_type); + + // eLanguageTypeHIP is not implemented as a DW_LNAME because of a conflict. + if (lang_type == lldb::eLanguageTypeHIP) + EXPECT_FALSE(lang); + else + EXPECT_TRUE(lang); + } + + EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus).GetDescription(), + "ISO C++"); + EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_17).GetDescription(), + "ISO C++"); + EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_20).GetDescription(), + "ISO C++"); + + EXPECT_EQ(SourceLanguage(eLanguageTypeObjC).GetDescription(), "Objective C"); + EXPECT_EQ(SourceLanguage(eLanguageTypeMipsAssembler).GetDescription(), + "Assembly"); + + auto next_vendor_language = + static_cast(eLanguageTypeMipsAssembler + 1); + if (next_vendor_language < eNumLanguageTypes) + EXPECT_NE(SourceLanguage(next_vendor_language).GetDescription(), "Unknown"); + + EXPECT_EQ(SourceLanguage(eLanguageTypeUnknown).GetDescription(), "Unknown"); +} + +TEST_F(LanguageTest, SourceLanguage_AsLanguageType) { + EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus).AsLanguageType(), + eLanguageTypeC_plus_plus); + EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_03).AsLanguageType(), + eLanguageTypeC_plus_plus_03); + + // Vendor-specific language code. + EXPECT_EQ(SourceLanguage(eLanguageTypeMipsAssembler).AsLanguageType(), + eLanguageTypeAssembly); + EXPECT_EQ(SourceLanguage(eLanguageTypeUnknown).AsLanguageType(), + eLanguageTypeUnknown); +} diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h index 231b7ac17d75f..7aaff7a781a5a 100644 --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -499,8 +499,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 0d17dc175fed9..e08678b655209 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: diff --git a/llvm/unittests/BinaryFormat/DwarfTest.cpp b/llvm/unittests/BinaryFormat/DwarfTest.cpp index 684e59fa2785c..f4519f61adf85 100644 --- a/llvm/unittests/BinaryFormat/DwarfTest.cpp +++ b/llvm/unittests/BinaryFormat/DwarfTest.cpp @@ -219,4 +219,77 @@ TEST(DwarfTest, lname) { EXPECT_EQ(roundtrip(DW_LANG_##NAME), DW_LANG_##NAME); #include "llvm/BinaryFormat/Dwarf.def" } + +TEST(DwarfTest, lname_getSourceLanguageName) { + // Some basics. + EXPECT_EQ(getSourceLanguageName("DW_LNAME_Ada"), DW_LNAME_Ada); + EXPECT_EQ(getSourceLanguageName("DW_LNAME_Metal"), DW_LNAME_Metal); + + // Test invalid input. + EXPECT_EQ(getSourceLanguageName(""), 0U); + EXPECT_EQ(getSourceLanguageName("blah"), 0U); + EXPECT_EQ(getSourceLanguageName("DW_LNAME__something_unlikely"), 0U); + EXPECT_EQ(getSourceLanguageName("DW_LANG_C"), 0U); + + // Test that we cover all DW_LNAME_ names. +#define xstr(X) #X +#define HANDLE_DW_LNAME(ID, NAME, DESC, LOWER_BOUND) \ + EXPECT_EQ(getSourceLanguageName(xstr(DW_LNAME_##NAME)), DW_LNAME_##NAME); +#include "llvm/BinaryFormat/Dwarf.def" +} + +TEST(DwarfTest, lname_SourceLanguageNameString) { + // Some basics. + EXPECT_EQ(SourceLanguageNameString(DW_LNAME_C_plus_plus), + "DW_LNAME_C_plus_plus"); + EXPECT_EQ(SourceLanguageNameString(DW_LNAME_CPP_for_OpenCL), + "DW_LNAME_CPP_for_OpenCL"); + + // Test invalid input. + EXPECT_EQ(SourceLanguageNameString(static_cast(0)), ""); + + // Test that we cover all DW_LNAME_ names. +#define xstr(X) #X +#define HANDLE_DW_LNAME(ID, NAME, DESC, LOWER_BOUND) \ + EXPECT_EQ(SourceLanguageNameString(DW_LNAME_##NAME), xstr(DW_LNAME_##NAME)); +#include "llvm/BinaryFormat/Dwarf.def" +} + +TEST(DWARFDebugInfo, TestLanguageDescription_Versioned) { + // Tests for the llvm::dwarf::LanguageDescription API that + // takes a name *and* a version. + + // Unknown language. + EXPECT_EQ( + llvm::dwarf::LanguageDescription(static_cast(0)), + "Unknown"); + + EXPECT_EQ( + llvm::dwarf::LanguageDescription(static_cast(0), 0), + "Unknown"); + + // Test that specifying an invalid version falls back to a valid language name + // regardless. + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_ObjC, 0), "Objective C"); + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_Julia, 0), "Julia"); + + // Check some versions. + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 199711), + "C++98"); + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 201402), + "C++14"); + + // Versions round up. + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 201400), + "C++14"); + + // Version 0 for C and C++ is an unversioned name. + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C, 0), "C (K&R and ISO)"); + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_C_plus_plus, 0), + "ISO C++"); + + // Version 0 for other versioned languages may not be the unversioned name. + EXPECT_EQ(llvm::dwarf::LanguageDescription(DW_LNAME_Fortran, 0), + "FORTRAN 77"); +} } // end namespace