[Lldb-commits] [lldb] r372203 - [lldb] Print better diagnostics for user expressions and modules

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 18 01:53:35 PDT 2019


Author: teemperor
Date: Wed Sep 18 01:53:35 2019
New Revision: 372203

URL: http://llvm.org/viewvc/llvm-project?rev=372203&view=rev
Log:
[lldb] Print better diagnostics for user expressions and modules

Summary:
Currently our expression evaluators only prints very basic errors that are not very useful when writing complex expressions.

For example, in the expression below the user made a type error, but it's not clear from the diagnostic what went wrong:
```
(lldb) expr printf("Modulos are:", foobar%mo1, foobar%mo2, foobar%mo3)
error: invalid operands to binary expression ('int' and 'double')
```

This patch enables full Clang diagnostics in our expression evaluator. After this patch the diagnostics for the expression look like this:

```
(lldb) expr printf("Modulos are:", foobar%mo1, foobar%mo2, foobar%mo3)
error: <user expression 1>:1:54: invalid operands to binary expression ('int' and 'float')
printf("Modulos are:", foobar%mo1, foobar%mo2, foobar%mo3)
                                               ~~~~~~^~~~
```

To make this possible, we now emulate a user expression file within our diagnostics. This prevents that the user is exposed to
our internal wrapper code we inject.

Note that the diagnostics that refer to declarations from the debug information (e.g. 'note' diagnostics pointing to a called function)
will not be improved by this as they don't have any source locations associated with them, so caret or line printing isn't possible.
We instead just suppress these diagnostics as we already do with warnings as they would otherwise just be a context message
without any context (and the original diagnostic in the user expression should be enough to explain the issue).

Fixes rdar://24306342

Reviewers: JDevlieghere, aprantl, shafik, #lldb

Reviewed By: JDevlieghere, #lldb

Subscribers: usaxena95, davide, jingham, aprantl, arphaman, kadircet, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D65646

Added:
    lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/
    lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/TestExprDiagnostics.py
    lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/main.cpp
Modified:
    lldb/trunk/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h

Added: lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/Makefile?rev=372203&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/Makefile Wed Sep 18 01:53:35 2019
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules

Added: lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/TestExprDiagnostics.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/TestExprDiagnostics.py?rev=372203&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/TestExprDiagnostics.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/TestExprDiagnostics.py Wed Sep 18 01:53:35 2019
@@ -0,0 +1,113 @@
+"""
+Test the diagnostics emitted by our embeded Clang instance that parses expressions.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class ExprDiagnosticsTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+        self.main_source = "main.cpp"
+        self.main_source_spec = lldb.SBFileSpec(self.main_source)
+
+    def test_source_and_caret_printing(self):
+        """Test that the source and caret positions LLDB prints are correct"""
+        self.build()
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+                                          '// Break here', self.main_source_spec)
+        frame = thread.GetFrameAtIndex(0)
+
+        # Test that source/caret are at the right position.
+        value = frame.EvaluateExpression("unknown_identifier")
+        self.assertFalse(value.GetError().Success())
+        # We should get a nice diagnostic with a caret pointing at the start of
+        # the identifier.
+        self.assertIn("\nunknown_identifier\n^\n", value.GetError().GetCString())
+        self.assertIn("<user expression 0>:1:1", value.GetError().GetCString())
+
+        # Same as above but with the identifier in the middle.
+        value = frame.EvaluateExpression("1 + unknown_identifier  ")
+        self.assertFalse(value.GetError().Success())
+        self.assertIn("\n1 + unknown_identifier", value.GetError().GetCString())
+        self.assertIn("\n    ^\n", value.GetError().GetCString())
+
+        # Multiline expressions.
+        value = frame.EvaluateExpression("int a = 0;\nfoobar +=1;\na")
+        self.assertFalse(value.GetError().Success())
+        # We should still get the right line information and caret position.
+        self.assertIn("\nfoobar +=1;\n^\n", value.GetError().GetCString())
+        # It's the second line of the user expression.
+        self.assertIn("<user expression 2>:2:1", value.GetError().GetCString())
+
+        # Top-level expressions.
+        top_level_opts = lldb.SBExpressionOptions();
+        top_level_opts.SetTopLevel(True)
+
+        value = frame.EvaluateExpression("void foo(unknown_type x) {}", top_level_opts)
+        self.assertFalse(value.GetError().Success())
+        self.assertIn("\nvoid foo(unknown_type x) {}\n         ^\n", value.GetError().GetCString())
+        # Top-level expressions might use a different wrapper code, but the file name should still
+        # be the same.
+        self.assertIn("<user expression 3>:1:10", value.GetError().GetCString())
+
+        # Multiline top-level expressions.
+        value = frame.EvaluateExpression("void x() {}\nvoid foo(unknown_type x) {}", top_level_opts)
+        self.assertFalse(value.GetError().Success())
+        self.assertIn("\nvoid foo(unknown_type x) {}\n         ^\n", value.GetError().GetCString())
+        self.assertIn("<user expression 4>:2:10", value.GetError().GetCString())
+
+        # Test that we render Clang's 'notes' correctly.
+        value = frame.EvaluateExpression("struct SFoo{}; struct SFoo { int x; };", top_level_opts)
+        self.assertFalse(value.GetError().Success())
+        self.assertIn("<user expression 5>:1:8: previous definition is here\nstruct SFoo{}; struct SFoo { int x; };\n       ^\n", value.GetError().GetCString())
+
+        # Declarations from the debug information currently have no debug information. It's not clear what
+        # we should do in this case, but we should at least not print anything that's wrong.
+        # In the future our declarations should have valid source locations.
+        value = frame.EvaluateExpression("struct FooBar { double x };", top_level_opts)
+        self.assertFalse(value.GetError().Success())
+        self.assertEqual("error: <user expression 6>:1:8: redefinition of 'FooBar'\nstruct FooBar { double x };\n       ^\n", value.GetError().GetCString())
+
+        value = frame.EvaluateExpression("foo(1, 2)")
+        self.assertFalse(value.GetError().Success())
+        self.assertEqual("error: <user expression 7>:1:1: no matching function for call to 'foo'\nfoo(1, 2)\n^~~\nnote: candidate function not viable: requires single argument 'x', but 2 arguments were provided\n\n", value.GetError().GetCString())
+
+        # Redefine something that we defined in a user-expression. We should use the previous expression file name
+        # for the original decl.
+        value = frame.EvaluateExpression("struct Redef { double x; };", top_level_opts)
+        value = frame.EvaluateExpression("struct Redef { float y; };", top_level_opts)
+        self.assertFalse(value.GetError().Success())
+        self.assertIn("error: <user expression 9>:1:8: redefinition of 'Redef'\nstruct Redef { float y; };\n       ^\n<user expression 8>:1:8: previous definition is here\nstruct Redef { double x; };\n       ^", value.GetError().GetCString())
+
+    @skipUnlessDarwin
+    def test_source_locations_from_objc_modules(self):
+        self.build()
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+                                          '// Break here', self.main_source_spec)
+        frame = thread.GetFrameAtIndex(0)
+
+        # Import foundation so that the Obj-C module is loaded (which contains source locations
+        # that can be used by LLDB).
+        self.runCmd("expr @import Foundation")
+        value = frame.EvaluateExpression("NSLog(1);")
+        self.assertFalse(value.GetError().Success())
+        print(value.GetError().GetCString())
+        # LLDB should print the source line that defines NSLog. To not rely on any
+        # header paths/line numbers or the actual formatting of the Foundation headers, only look
+        # for a few tokens in the output.
+        # File path should come from Foundation framework.
+        self.assertIn("/Foundation.framework/", value.GetError().GetCString())
+        # The NSLog definition source line should be printed. Return value and
+        # the first argument are probably stable enough that this test can check for them.
+        self.assertIn("void NSLog(NSString *format", value.GetError().GetCString())
+

Added: lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/main.cpp?rev=372203&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/commands/expression/diagnostics/main.cpp Wed Sep 18 01:53:35 2019
@@ -0,0 +1,11 @@
+void foo(int x) {}
+
+struct FooBar {
+  int i;
+};
+
+int main() {
+  FooBar f;
+  foo(1);
+  return 0; // Break here
+}

Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lang/objc/foundation/TestObjCMethods.py Wed Sep 18 01:53:35 2019
@@ -191,7 +191,7 @@ class FoundationTestCase(TestBase):
             "expression self->non_existent_member",
             COMMAND_FAILED_AS_EXPECTED,
             error=True,
-            startstr="error: 'MyString' does not have a member named 'non_existent_member'")
+            substrs=["error:", "'MyString' does not have a member named 'non_existent_member'"])
 
         # Use expression parser.
         self.runCmd("expression self->str")

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangDiagnostic.h Wed Sep 18 01:53:35 2019
@@ -29,7 +29,7 @@ public:
     return diag->getKind() == eDiagnosticOriginClang;
   }
 
-  ClangDiagnostic(const char *message, DiagnosticSeverity severity,
+  ClangDiagnostic(llvm::StringRef message, DiagnosticSeverity severity,
                   uint32_t compiler_id)
       : Diagnostic(message, severity, eDiagnosticOriginClang, compiler_id) {}
 

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp Wed Sep 18 01:53:35 2019
@@ -137,12 +137,14 @@ public:
 
 class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer {
 public:
-  ClangDiagnosticManagerAdapter()
-      : m_passthrough(new clang::TextDiagnosticBuffer) {}
-
-  ClangDiagnosticManagerAdapter(
-      const std::shared_ptr<clang::TextDiagnosticBuffer> &passthrough)
-      : m_passthrough(passthrough) {}
+  ClangDiagnosticManagerAdapter(DiagnosticOptions &opts) {
+    DiagnosticOptions *m_options = new DiagnosticOptions(opts);
+    m_options->ShowPresumedLoc = true;
+    m_options->ShowLevel = false;
+    m_os.reset(new llvm::raw_string_ostream(m_output));
+    m_passthrough.reset(
+        new clang::TextDiagnosticPrinter(*m_os, m_options, false));
+  }
 
   void ResetManager(DiagnosticManager *manager = nullptr) {
     m_manager = manager;
@@ -150,12 +152,12 @@ public:
 
   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
                         const clang::Diagnostic &Info) override {
-    if (m_manager) {
-      llvm::SmallVector<char, 32> diag_str;
-      Info.FormatDiagnostic(diag_str);
-      diag_str.push_back('\0');
-      const char *data = diag_str.data();
+    // Render diagnostic message to m_output.
+    m_output.clear();
+    m_passthrough->HandleDiagnostic(DiagLevel, Info);
+    m_os->flush();
 
+    if (m_manager) {
       lldb_private::DiagnosticSeverity severity;
       bool make_new_diagnostic = true;
 
@@ -172,12 +174,16 @@ public:
         severity = eDiagnosticSeverityRemark;
         break;
       case DiagnosticsEngine::Level::Note:
-        m_manager->AppendMessageToDiagnostic(data);
+        m_manager->AppendMessageToDiagnostic(m_output);
         make_new_diagnostic = false;
       }
       if (make_new_diagnostic) {
+        // ClangDiagnostic messages are expected to have no whitespace/newlines
+        // around them.
+        std::string stripped_output = llvm::StringRef(m_output).trim();
+
         ClangDiagnostic *new_diagnostic =
-            new ClangDiagnostic(data, severity, Info.getID());
+            new ClangDiagnostic(stripped_output, severity, Info.getID());
         m_manager->AddDiagnostic(new_diagnostic);
 
         // Don't store away warning fixits, since the compiler doesn't have
@@ -194,23 +200,17 @@ public:
         }
       }
     }
-
-    m_passthrough->HandleDiagnostic(DiagLevel, Info);
-  }
-
-  void FlushDiagnostics(DiagnosticsEngine &Diags) {
-    m_passthrough->FlushDiagnostics(Diags);
-  }
-
-  DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
-    return new ClangDiagnosticManagerAdapter(m_passthrough);
   }
 
-  clang::TextDiagnosticBuffer *GetPassthrough() { return m_passthrough.get(); }
+  clang::TextDiagnosticPrinter *GetPassthrough() { return m_passthrough.get(); }
 
 private:
   DiagnosticManager *m_manager = nullptr;
-  std::shared_ptr<clang::TextDiagnosticBuffer> m_passthrough;
+  std::shared_ptr<clang::TextDiagnosticPrinter> m_passthrough;
+  /// Output stream of m_passthrough.
+  std::shared_ptr<llvm::raw_string_ostream> m_os;
+  /// Output string filled by m_os.
+  std::string m_output;
 };
 
 static void SetupModuleHeaderPaths(CompilerInstance *compiler,
@@ -258,12 +258,11 @@ static void SetupModuleHeaderPaths(Compi
 // Implementation of ClangExpressionParser
 //===----------------------------------------------------------------------===//
 
-ClangExpressionParser::ClangExpressionParser(
-    ExecutionContextScope *exe_scope, Expression &expr,
-    bool generate_debug_info, std::vector<std::string> include_directories)
+ClangExpressionParser::ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
+    bool generate_debug_info, 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)) {
+      m_include_directories(std::move(include_directories)), m_filename(std::move(filename)) {
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
 
   // We can't compile expressions without a target.  So if the exe_scope is
@@ -557,7 +556,9 @@ ClangExpressionParser::ClangExpressionPa
 
   // 6. Set up the diagnostic buffer for reporting errors
 
-  m_compiler->getDiagnostics().setClient(new ClangDiagnosticManagerAdapter);
+  auto diag_mgr = new ClangDiagnosticManagerAdapter(
+      m_compiler->getDiagnostics().getDiagnosticOptions());
+  m_compiler->getDiagnostics().setClient(diag_mgr);
 
   // 7. Set up the source management objects inside the compiler
   m_compiler->createFileManager();
@@ -869,8 +870,7 @@ ClangExpressionParser::ParseInternal(Dia
   ClangDiagnosticManagerAdapter *adapter =
       static_cast<ClangDiagnosticManagerAdapter *>(
           m_compiler->getDiagnostics().getClient());
-  clang::TextDiagnosticBuffer *diag_buf = adapter->GetPassthrough();
-  diag_buf->FlushDiagnostics(m_compiler->getDiagnostics());
+  auto diag_buf = adapter->GetPassthrough();
 
   adapter->ResetManager(&diagnostic_manager);
 
@@ -921,7 +921,7 @@ ClangExpressionParser::ParseInternal(Dia
 
   if (!created_main_file) {
     std::unique_ptr<MemoryBuffer> memory_buffer =
-        MemoryBuffer::getMemBufferCopy(expr_text, "<lldb-expr>");
+        MemoryBuffer::getMemBufferCopy(expr_text, m_filename);
     source_mgr.setMainFileID(source_mgr.createFileID(std::move(memory_buffer)));
   }
 

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h Wed Sep 18 01:53:35 2019
@@ -53,9 +53,14 @@ public:
   /// @param[in] include_directories
   ///     List of include directories that should be used when parsing the
   ///     expression.
+  ///
+  /// @param[in] filename
+  ///     Name of the source file that should be used when rendering
+  ///     diagnostics (i.e. errors, warnings or notes from Clang).
   ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
                         bool generate_debug_info,
-                        std::vector<std::string> include_directories = {});
+                        std::vector<std::string> include_directories = {},
+                        std::string filename = "<clang expression>");
 
   /// Destructor
   ~ClangExpressionParser() override;
@@ -178,6 +183,8 @@ private:
   std::unique_ptr<ClangASTContext> m_ast_context;
 
   std::vector<std::string> m_include_directories;
+  /// File name used for the user expression.
+  std::string m_filename;
 };
 }
 

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp Wed Sep 18 01:53:35 2019
@@ -29,7 +29,9 @@
 
 using namespace lldb_private;
 
-const char *ClangExpressionSourceCode::g_expression_prefix = R"(
+const char *ClangExpressionSourceCode::g_expression_prefix =
+    R"(
+#line 1 "<lldb wrapper prefix>"
 #ifndef offsetof
 #define offsetof(t, d) __builtin_offsetof(t, d)
 #endif
@@ -67,9 +69,6 @@ extern "C"
 }
 )";
 
-static const char *c_start_marker = "    /*LLDB_BODY_START*/\n    ";
-static const char *c_end_marker = ";\n    /*LLDB_BODY_END*/\n";
-
 namespace {
 
 class AddMacroState {
@@ -169,6 +168,17 @@ static void AddMacros(const DebugMacros
   }
 }
 
+lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode(
+    llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix,
+    llvm::StringRef body, Wrapping wrap)
+    : ExpressionSourceCode(name, prefix, body, wrap) {
+  // Use #line markers to pretend that we have a single-line source file
+  // containing only the user expression. This will hide our wrapper code
+  // from the user when we render diagnostics with Clang.
+  m_start_marker = "#line 1 \"" + filename.str() + "\"\n";
+  m_end_marker = "\n;\n#line 1 \"<lldb wrapper suffix>\"\n";
+}
+
 namespace {
 /// Allows checking if a token is contained in a given expression.
 class TokenVerifier {
@@ -401,9 +411,9 @@ bool ClangExpressionSourceCode::GetText(
     case lldb::eLanguageTypeC:
     case lldb::eLanguageTypeC_plus_plus:
     case lldb::eLanguageTypeObjC:
-      tagged_body.append(c_start_marker);
+      tagged_body.append(m_start_marker);
       tagged_body.append(m_body);
-      tagged_body.append(c_end_marker);
+      tagged_body.append(m_end_marker);
       break;
     }
     switch (wrapping_language) {
@@ -477,24 +487,19 @@ bool ClangExpressionSourceCode::GetText(
 bool ClangExpressionSourceCode::GetOriginalBodyBounds(
     std::string transformed_text, lldb::LanguageType wrapping_language,
     size_t &start_loc, size_t &end_loc) {
-  const char *start_marker;
-  const char *end_marker;
-
   switch (wrapping_language) {
   default:
     return false;
   case lldb::eLanguageTypeC:
   case lldb::eLanguageTypeC_plus_plus:
   case lldb::eLanguageTypeObjC:
-    start_marker = c_start_marker;
-    end_marker = c_end_marker;
     break;
   }
 
-  start_loc = transformed_text.find(start_marker);
+  start_loc = transformed_text.find(m_start_marker);
   if (start_loc == std::string::npos)
     return false;
-  start_loc += strlen(start_marker);
-  end_loc = transformed_text.find(end_marker);
+  start_loc += m_start_marker.size();
+  end_loc = transformed_text.find(m_end_marker);
   return end_loc != std::string::npos;
 }

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h Wed Sep 18 01:53:35 2019
@@ -25,9 +25,11 @@ class ClangExpressionSourceCode : public
 public:
   static const char *g_expression_prefix;
 
-  static ClangExpressionSourceCode *CreateWrapped(llvm::StringRef prefix,
+  static ClangExpressionSourceCode *CreateWrapped(llvm::StringRef filename,
+                                                  llvm::StringRef prefix,
                                                   llvm::StringRef body) {
-    return new ClangExpressionSourceCode("$__lldb_expr", prefix, body, Wrap);
+    return new ClangExpressionSourceCode(filename, "$__lldb_expr", prefix, body,
+                                         Wrap);
   }
 
   /// Generates the source code that will evaluate the expression.
@@ -54,14 +56,20 @@ public:
   // Given a string returned by GetText, find the beginning and end of the body
   // passed to CreateWrapped. Return true if the bounds could be found.  This
   // will also work on text with FixItHints applied.
-  static bool GetOriginalBodyBounds(std::string transformed_text,
-                                    lldb::LanguageType wrapping_language,
-                                    size_t &start_loc, size_t &end_loc);
+  bool GetOriginalBodyBounds(std::string transformed_text,
+                             lldb::LanguageType wrapping_language,
+                             size_t &start_loc, size_t &end_loc);
 
 protected:
-  ClangExpressionSourceCode(llvm::StringRef name, llvm::StringRef prefix,
-                            llvm::StringRef body, Wrapping wrap)
-      : ExpressionSourceCode(name, prefix, body, wrap) {}
+  ClangExpressionSourceCode(llvm::StringRef filename, llvm::StringRef name,
+                            llvm::StringRef prefix, llvm::StringRef body,
+                            Wrapping wrap);
+
+private:
+  /// String marking the start of the user expression.
+  std::string m_start_marker;
+  /// String marking the end of the user expression.
+  std::string m_end_marker;
 };
 
 } // namespace lldb_private

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.h Wed Sep 18 01:53:35 2019
@@ -50,6 +50,15 @@ public:
     return "$";
   }
 
+  /// Returns the next file name that should be used for user expressions.
+  std::string GetNextExprFileName() {
+    std::string name;
+    name.append("<user expression ");
+    name.append(std::to_string(m_next_user_file_id++));
+    name.append(">");
+    return name;
+  }
+
   llvm::Optional<CompilerType>
   GetCompilerTypeFromPersistentDecl(ConstString type_name) override;
 
@@ -66,6 +75,8 @@ public:
   }
 
 private:
+  /// The counter used by GetNextExprFileName.
+  uint32_t m_next_user_file_id = 0;
   // The counter used by GetNextPersistentVariableName
   uint32_t m_next_persistent_variable_id = 0;
 

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp Wed Sep 18 01:53:35 2019
@@ -23,7 +23,6 @@
 #include "ClangDiagnostic.h"
 #include "ClangExpressionDeclMap.h"
 #include "ClangExpressionParser.h"
-#include "ClangExpressionSourceCode.h"
 #include "ClangModulesDeclVendor.h"
 #include "ClangPersistentVariables.h"
 
@@ -328,6 +327,7 @@ bool ClangUserExpression::SetupPersisten
     if (PersistentExpressionState *persistent_state =
             target->GetPersistentExpressionStateForLanguage(
                 lldb::eLanguageTypeC)) {
+      m_clang_state = llvm::cast<ClangPersistentVariables>(persistent_state);
       m_result_delegate.RegisterPersistentState(persistent_state);
     } else {
       diagnostic_manager.PutString(
@@ -392,18 +392,18 @@ void ClangUserExpression::CreateSourceCo
     DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
     std::vector<std::string> modules_to_import, bool for_completion) {
 
+  m_filename = m_clang_state->GetNextExprFileName();
   std::string prefix = m_expr_prefix;
 
   if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
     m_transformed_text = m_expr_text;
   } else {
-    std::unique_ptr<ClangExpressionSourceCode> source_code(
-        ClangExpressionSourceCode::CreateWrapped(prefix.c_str(),
-                                                 m_expr_text.c_str()));
-
-    if (!source_code->GetText(m_transformed_text, m_expr_lang,
-                              m_in_static_method, exe_ctx, !m_ctx_obj,
-                              for_completion, modules_to_import)) {
+    m_source_code.reset(ClangExpressionSourceCode::CreateWrapped(
+        m_filename, prefix.c_str(), m_expr_text.c_str()));
+
+    if (!m_source_code->GetText(m_transformed_text, m_expr_lang,
+                                m_in_static_method, exe_ctx, !m_ctx_obj,
+                                for_completion, modules_to_import)) {
       diagnostic_manager.PutString(eDiagnosticSeverityError,
                                    "couldn't construct expression body");
       return;
@@ -413,7 +413,7 @@ void ClangUserExpression::CreateSourceCo
     // transformed code. We need this later for the code completion.
     std::size_t original_start;
     std::size_t original_end;
-    bool found_bounds = source_code->GetOriginalBodyBounds(
+    bool found_bounds = m_source_code->GetOriginalBodyBounds(
         m_transformed_text, m_expr_lang, original_start, original_end);
     if (found_bounds)
       m_user_expression_start_pos = original_start;
@@ -568,7 +568,7 @@ bool ClangUserExpression::Parse(Diagnost
   // parser_sp will never be empty.
 
   ClangExpressionParser parser(exe_scope, *this, generate_debug_info,
-                               m_include_directories);
+                               m_include_directories, m_filename);
 
   unsigned num_errors = parser.Parse(diagnostic_manager);
 
@@ -581,8 +581,8 @@ bool ClangUserExpression::Parse(Diagnost
         size_t fixed_end;
         const std::string &fixed_expression =
             diagnostic_manager.GetFixedExpression();
-        if (ClangExpressionSourceCode::GetOriginalBodyBounds(
-                fixed_expression, m_expr_lang, fixed_start, fixed_end))
+        if (m_source_code->GetOriginalBodyBounds(fixed_expression, m_expr_lang,
+                                                 fixed_start, fixed_end))
           m_fixed_text =
               fixed_expression.substr(fixed_start, fixed_end - fixed_start);
       }

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h?rev=372203&r1=372202&r2=372203&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h Wed Sep 18 01:53:35 2019
@@ -15,6 +15,7 @@
 #include "ASTStructExtractor.h"
 #include "ClangExpressionDeclMap.h"
 #include "ClangExpressionHelper.h"
+#include "ClangExpressionSourceCode.h"
 #include "ClangExpressionVariable.h"
 #include "IRForTarget.h"
 
@@ -216,6 +217,10 @@ private:
   /// were not able to calculate this position.
   llvm::Optional<size_t> m_user_expression_start_pos;
   ResultDelegate m_result_delegate;
+  ClangPersistentVariables *m_clang_state;
+  std::unique_ptr<ClangExpressionSourceCode> m_source_code;
+  /// File name used for the expression.
+  std::string m_filename;
 
   /// The object (if any) in which context the expression is evaluated.
   /// See the comment to `UserExpression::Evaluate` for details.




More information about the lldb-commits mailing list