[Lldb-commits] [lldb] r341086 - Added initial code completion support for the `expr` command

Raphael Isemann via lldb-commits lldb-commits at lists.llvm.org
Thu Aug 30 10:29:38 PDT 2018


Author: teemperor
Date: Thu Aug 30 10:29:37 2018
New Revision: 341086

URL: http://llvm.org/viewvc/llvm-project?rev=341086&view=rev
Log:
Added initial code completion support for the `expr` command

Summary:
This patch adds initial code completion support for the `expr` command.

We now have a completion handler in the expression CommandObject that
essentially just attempts to parse the given user expression with Clang with
an attached code completion consumer. We filter and prepare the
code completions provided by Clang and send them back to the completion
API.

The current completion is limited to variables that are in the current scope.
This includes local variables and all types used by local variables. We however
don't do any completion of symbols that are not used in the local scope (or
in some other way already in the ASTContext).

This is partly because there is not yet any code that manually searches for additiona
information in the debug information. Another cause is that for some reason the existing
code for loading these additional symbols when requested by Clang doesn't seem to work.
This will be fixed in a future patch.

Reviewers: jingham, teemperor

Reviewed By: teemperor

Subscribers: labath, aprantl, JDevlieghere, friss, lldb-commits

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

Added:
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/.categories
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/main.cpp
    lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/other.cpp
Modified:
    lldb/trunk/include/lldb/Expression/ExpressionParser.h
    lldb/trunk/include/lldb/Expression/UserExpression.h
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py
    lldb/trunk/packages/Python/lldbsuite/test/lldbtest.py
    lldb/trunk/source/Commands/CommandObjectExpression.cpp
    lldb/trunk/source/Commands/CommandObjectExpression.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
    lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h

Modified: lldb/trunk/include/lldb/Expression/ExpressionParser.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Expression/ExpressionParser.h?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Expression/ExpressionParser.h (original)
+++ lldb/trunk/include/lldb/Expression/ExpressionParser.h Thu Aug 30 10:29:37 2018
@@ -50,6 +50,41 @@ public:
   virtual ~ExpressionParser(){};
 
   //------------------------------------------------------------------
+  /// Attempts to find possible command line completions for the given
+  /// expression.
+  ///
+  /// @param[out] matches
+  ///     The list of completions that should be appended with string
+  ///     that would complete the current token at the cursor position.
+  ///     Note that the string in the list replaces the current token
+  ///     in the command line.
+  ///
+  /// @param[in] line
+  ///     The line with the completion cursor inside the expression as a string.
+  ///     The first line in the expression has the number 0.
+  ///
+  /// @param[in] pos
+  ///     The character position in the line with the completion cursor.
+  ///     If the value is 0, then the cursor is on top of the first character
+  ///     in the line (i.e. the user has requested completion from the start of
+  ///     the expression).
+  ///
+  /// @param[in] typed_pos
+  ///     The cursor position in the line as typed by the user. If the user
+  ///     expression has not been transformed in some form (e.g. wrapping it
+  ///     in a function body for C languages), then this is equal to the
+  ///     'pos' parameter. The semantics of this value are otherwise equal to
+  ///     'pos' (e.g. a value of 0 means the cursor is at start of the
+  ///     expression).
+  ///
+  /// @return
+  ///     True if we added any completion results to the output;
+  ///     false otherwise.
+  //------------------------------------------------------------------
+  virtual bool Complete(StringList &matches, unsigned line, unsigned pos,
+                        unsigned typed_pos) = 0;
+
+  //------------------------------------------------------------------
   /// Parse a single expression and convert it to IR using Clang.  Don't wrap
   /// the expression in anything at all.
   ///

Modified: lldb/trunk/include/lldb/Expression/UserExpression.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Expression/UserExpression.h?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Expression/UserExpression.h (original)
+++ lldb/trunk/include/lldb/Expression/UserExpression.h Thu Aug 30 10:29:37 2018
@@ -98,6 +98,34 @@ public:
                      lldb_private::ExecutionPolicy execution_policy,
                      bool keep_result_in_memory, bool generate_debug_info) = 0;
 
+  //------------------------------------------------------------------
+  /// Attempts to find possible command line completions for the given
+  /// (possible incomplete) user expression.
+  ///
+  /// @param[in] exe_ctx
+  ///     The execution context to use when looking up entities that
+  ///     are needed for parsing and completing (locations of functions, types
+  ///     of variables, persistent variables, etc.)
+  ///
+  /// @param[out] matches
+  ///     The list of completions that should be appended with string
+  ///     that would complete the current token at the cursor position.
+  ///     Note that the string in the list replaces the current token
+  ///     in the command line.
+  ///
+  /// @param[in] complete_pos
+  ///     The position of the cursor inside the user expression string.
+  ///     The completion process starts on the token that the cursor is in.
+  ///
+  /// @return
+  ///     True if we added any completion results to the output;
+  ///     false otherwise.
+  //------------------------------------------------------------------
+  virtual bool Complete(ExecutionContext &exe_ctx, StringList &matches,
+                        unsigned complete_pos) {
+    return false;
+  }
+
   virtual bool CanInterpret() = 0;
 
   bool MatchesContext(ExecutionContext &exe_ctx);

Added: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/.categories
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/.categories?rev=341086&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/.categories (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/.categories Thu Aug 30 10:29:37 2018
@@ -0,0 +1 @@
+cmdline

Added: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/Makefile?rev=341086&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/Makefile Thu Aug 30 10:29:37 2018
@@ -0,0 +1,5 @@
+LEVEL = ../../make
+
+CXX_SOURCES := main.cpp other.cpp
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py?rev=341086&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py Thu Aug 30 10:29:37 2018
@@ -0,0 +1,227 @@
+"""
+Test the lldb command line completion mechanism for the 'expr' command.
+"""
+
+from __future__ import print_function
+
+import random
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbplatform
+from lldbsuite.test import lldbutil
+
+class CommandLineExprCompletionTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489")
+    def test_expr_completion(self):
+        self.build()
+        self.main_source = "main.cpp"
+        self.main_source_spec = lldb.SBFileSpec(self.main_source)
+        self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+
+        # Try the completion before we have a context to complete on.
+        self.assume_no_completions('expr some_expr')
+        self.assume_no_completions('expr ')
+        self.assume_no_completions('expr f')
+
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
+                                          '// Break here', self.main_source_spec)
+
+        # Completing member functions
+        self.complete_exactly('expr some_expr.FooNoArgs',
+                              'expr some_expr.FooNoArgsBar()')
+        self.complete_exactly('expr some_expr.FooWithArgs',
+                              'expr some_expr.FooWithArgsBar(')
+        self.complete_exactly('expr some_expr.FooWithMultipleArgs',
+                              'expr some_expr.FooWithMultipleArgsBar(')
+        self.complete_exactly('expr some_expr.FooUnderscore',
+                              'expr some_expr.FooUnderscoreBar_()')
+        self.complete_exactly('expr some_expr.FooNumbers',
+                              'expr some_expr.FooNumbersBar1()')
+        self.complete_exactly('expr some_expr.StaticMemberMethod',
+                              'expr some_expr.StaticMemberMethodBar()')
+
+        # Completing static functions
+        self.complete_exactly('expr Expr::StaticMemberMethod',
+                              'expr Expr::StaticMemberMethodBar()')
+
+        # Completing member variables
+        self.complete_exactly('expr some_expr.MemberVariab',
+                              'expr some_expr.MemberVariableBar')
+
+        # Multiple completions
+        self.completions_contain('expr some_expr.',
+                                 ['some_expr.FooNumbersBar1()',
+                                  'some_expr.FooUnderscoreBar_()',
+                                  'some_expr.FooWithArgsBar(',
+                                  'some_expr.MemberVariableBar'])
+
+        self.completions_contain('expr some_expr.Foo',
+                                 ['some_expr.FooNumbersBar1()',
+                                  'some_expr.FooUnderscoreBar_()',
+                                  'some_expr.FooWithArgsBar('])
+
+        self.completions_contain('expr ',
+                                 ['static_cast',
+                                  'reinterpret_cast',
+                                  'dynamic_cast'])
+
+        self.completions_contain('expr 1 + ',
+                                 ['static_cast',
+                                  'reinterpret_cast',
+                                  'dynamic_cast'])
+
+        # Completion expr without spaces
+        # This is a bit awkward looking for the user, but that's how
+        # the completion API works at the moment.
+        self.completions_contain('expr 1+',
+                                 ['1+some_expr', "1+static_cast"])
+
+        # Test with spaces
+        self.complete_exactly('expr   some_expr .FooNoArgs',
+                              'expr   some_expr .FooNoArgsBar()')
+        self.complete_exactly('expr  some_expr .FooNoArgs',
+                              'expr  some_expr .FooNoArgsBar()')
+        self.complete_exactly('expr some_expr .FooNoArgs',
+                              'expr some_expr .FooNoArgsBar()')
+        self.complete_exactly('expr some_expr. FooNoArgs',
+                              'expr some_expr. FooNoArgsBar()')
+        self.complete_exactly('expr some_expr . FooNoArgs',
+                              'expr some_expr . FooNoArgsBar()')
+        self.complete_exactly('expr Expr :: StaticMemberMethod',
+                              'expr Expr :: StaticMemberMethodBar()')
+        self.complete_exactly('expr Expr ::StaticMemberMethod',
+                              'expr Expr ::StaticMemberMethodBar()')
+        self.complete_exactly('expr Expr:: StaticMemberMethod',
+                              'expr Expr:: StaticMemberMethodBar()')
+
+        # Test that string literals don't break our parsing logic.
+        self.complete_exactly('expr const char *cstr = "some_e"; char c = *cst',
+                              'expr const char *cstr = "some_e"; char c = *cstr')
+        self.complete_exactly('expr const char *cstr = "some_e" ; char c = *cst',
+                              'expr const char *cstr = "some_e" ; char c = *cstr')
+        # Requesting completions inside an incomplete string doesn't provide any
+        # completions.
+        self.complete_exactly('expr const char *cstr = "some_e',
+                              'expr const char *cstr = "some_e')
+
+        # Completing inside double dash should do nothing
+        self.assume_no_completions('expr -i0 -- some_expr.', 10)
+        self.assume_no_completions('expr -i0 -- some_expr.', 11)
+
+        # Test with expr arguments
+        self.complete_exactly('expr -i0 -- some_expr .FooNoArgs',
+                              'expr -i0 -- some_expr .FooNoArgsBar()')
+        self.complete_exactly('expr  -i0 -- some_expr .FooNoArgs',
+                              'expr  -i0 -- some_expr .FooNoArgsBar()')
+
+        # Addrof and deref
+        self.complete_exactly('expr (*(&some_expr)).FooNoArgs',
+                              'expr (*(&some_expr)).FooNoArgsBar()')
+        self.complete_exactly('expr (*(&some_expr)) .FooNoArgs',
+                              'expr (*(&some_expr)) .FooNoArgsBar()')
+        self.complete_exactly('expr (* (&some_expr)) .FooNoArgs',
+                              'expr (* (&some_expr)) .FooNoArgsBar()')
+        self.complete_exactly('expr (* (& some_expr)) .FooNoArgs',
+                              'expr (* (& some_expr)) .FooNoArgsBar()')
+
+        # Addrof and deref (part 2)
+        self.complete_exactly('expr (&some_expr)->FooNoArgs',
+                              'expr (&some_expr)->FooNoArgsBar()')
+        self.complete_exactly('expr (&some_expr) ->FooNoArgs',
+                              'expr (&some_expr) ->FooNoArgsBar()')
+        self.complete_exactly('expr (&some_expr) -> FooNoArgs',
+                              'expr (&some_expr) -> FooNoArgsBar()')
+        self.complete_exactly('expr (&some_expr)-> FooNoArgs',
+                              'expr (&some_expr)-> FooNoArgsBar()')
+
+        # Builtin arg
+        self.complete_exactly('expr static_ca',
+                              'expr static_cast')
+
+        # From other files
+        self.complete_exactly('expr fwd_decl_ptr->Hidden',
+                              'expr fwd_decl_ptr->HiddenMember')
+
+
+        # Types
+        self.complete_exactly('expr LongClassNa',
+                              'expr LongClassName')
+        self.complete_exactly('expr LongNamespaceName::NestedCla',
+                              'expr LongNamespaceName::NestedClass')
+
+        # Namespaces
+        self.complete_exactly('expr LongNamespaceNa',
+                              'expr LongNamespaceName::')
+
+        # Multiple arguments
+        self.complete_exactly('expr &some_expr + &some_e',
+                              'expr &some_expr + &some_expr')
+        self.complete_exactly('expr SomeLongVarNameWithCapitals + SomeLongVarName',
+                              'expr SomeLongVarNameWithCapitals + SomeLongVarNameWithCapitals')
+        self.complete_exactly('expr SomeIntVar + SomeIntV',
+                              'expr SomeIntVar + SomeIntVar')
+
+        # Multiple statements
+        self.complete_exactly('expr long LocalVariable = 0; LocalVaria',
+                              'expr long LocalVariable = 0; LocalVariable')
+
+        # Custom Decls
+        self.complete_exactly('expr auto l = [](int LeftHandSide, int bx){ return LeftHandS',
+                              'expr auto l = [](int LeftHandSide, int bx){ return LeftHandSide')
+        self.complete_exactly('expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.Mem',
+                              'expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.MemberName')
+
+        # Completing function call arguments
+        self.complete_exactly('expr some_expr.FooWithArgsBar(some_exp',
+                              'expr some_expr.FooWithArgsBar(some_expr')
+        self.complete_exactly('expr some_expr.FooWithArgsBar(SomeIntV',
+                              'expr some_expr.FooWithArgsBar(SomeIntVar')
+        self.complete_exactly('expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVa',
+                              'expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVar')
+
+        # Function return values
+        self.complete_exactly('expr some_expr.Self().FooNoArgs',
+                              'expr some_expr.Self().FooNoArgsBar()')
+        self.complete_exactly('expr some_expr.Self() .FooNoArgs',
+                              'expr some_expr.Self() .FooNoArgsBar()')
+        self.complete_exactly('expr some_expr.Self(). FooNoArgs',
+                              'expr some_expr.Self(). FooNoArgsBar()')
+
+    def assume_no_completions(self, str_input, cursor_pos = None):
+        interp = self.dbg.GetCommandInterpreter()
+        match_strings = lldb.SBStringList()
+        if cursor_pos is None:
+          cursor_pos = len(str_input)
+        num_matches = interp.HandleCompletion(str_input, cursor_pos, 0, -1, match_strings)
+
+        available_completions = []
+        for m in match_strings:
+            available_completions.append(m)
+
+        self.assertEquals(num_matches, 0, "Got matches, but didn't expect any: " + str(available_completions))
+
+    def completions_contain(self, str_input, items):
+        interp = self.dbg.GetCommandInterpreter()
+        match_strings = lldb.SBStringList()
+        num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings)
+        common_match = match_strings.GetStringAtIndex(0)
+
+        for item in items:
+            found = False
+            for m in match_strings:
+                if m == item:
+                    found = True
+            if not found:
+                # Transform match_strings to a python list with strings
+                available_completions = []
+                for m in match_strings:
+                     available_completions.append(m)
+                self.assertTrue(found, "Couldn't find completion " + item + " in completions " + str(available_completions))

Added: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/main.cpp?rev=341086&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/main.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/main.cpp Thu Aug 30 10:29:37 2018
@@ -0,0 +1,35 @@
+namespace LongNamespaceName { class NestedClass { long m; }; }
+
+// Defined in other.cpp, we only have a forward declaration here.
+struct ForwardDecl;
+extern ForwardDecl fwd_decl;
+
+class LongClassName { long i ; };
+
+class Expr {
+public:
+    int FooNoArgsBar() { return 1; }
+    int FooWithArgsBar(int i) { return i; }
+    int FooWithMultipleArgsBar(int i, int j) { return i + j; }
+    int FooUnderscoreBar_() { return 4; }
+    int FooNumbersBar1() { return 8; }
+    int MemberVariableBar = 0;
+    Expr &Self() { return *this; }
+    static int StaticMemberMethodBar() { return 82; }
+};
+
+int main()
+{
+    LongClassName a;
+    LongNamespaceName::NestedClass NestedFoo;
+    long SomeLongVarNameWithCapitals = 44;
+    int SomeIntVar = 33;
+    Expr some_expr;
+    some_expr.FooNoArgsBar();
+    some_expr.FooWithArgsBar(1);
+    some_expr.FooUnderscoreBar_();
+    some_expr.FooNumbersBar1();
+    Expr::StaticMemberMethodBar();
+    ForwardDecl *fwd_decl_ptr = &fwd_decl;
+    return 0; // Break here
+}

Added: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/other.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/other.cpp?rev=341086&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/other.cpp (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/other.cpp Thu Aug 30 10:29:37 2018
@@ -0,0 +1,4 @@
+struct ForwardDecl {
+   long HiddenMemberName;
+};
+ForwardDecl fwd_decl;

Modified: lldb/trunk/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py Thu Aug 30 10:29:37 2018
@@ -281,39 +281,3 @@ class CommandLineCompletionTestCase(Test
         self.complete_from_to('breakpoint set -n Fo',
                               'breakpoint set -n Foo::Bar(int,\\ int)',
                               turn_off_re_match=True)
-
-    def complete_from_to(self, str_input, patterns, turn_off_re_match=False):
-        """Test that the completion mechanism completes str_input to patterns,
-        where patterns could be a pattern-string or a list of pattern-strings"""
-        # Patterns should not be None in order to proceed.
-        self.assertFalse(patterns is None)
-        # And should be either a string or list of strings.  Check for list type
-        # below, if not, make a list out of the singleton string.  If patterns
-        # is not a string or not a list of strings, there'll be runtime errors
-        # later on.
-        if not isinstance(patterns, list):
-            patterns = [patterns]
-
-        interp = self.dbg.GetCommandInterpreter()
-        match_strings = lldb.SBStringList()
-        num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings)
-        common_match = match_strings.GetStringAtIndex(0)
-        if num_matches == 0:
-            compare_string = str_input
-        else:
-            if common_match != None and len(common_match) > 0:
-                compare_string = str_input + common_match
-            else:
-                compare_string = ""
-                for idx in range(1, num_matches+1):
-                    compare_string += match_strings.GetStringAtIndex(idx) + "\n"
-
-        for p in patterns:
-            if turn_off_re_match:
-                self.expect(
-                    compare_string, msg=COMPLETION_MSG(
-                        str_input, p, match_strings), exe=False, substrs=[p])
-            else:
-                self.expect(
-                    compare_string, msg=COMPLETION_MSG(
-                        str_input, p, match_strings), exe=False, patterns=[p])

Modified: lldb/trunk/packages/Python/lldbsuite/test/lldbtest.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lldbtest.py?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/lldbtest.py (original)
+++ lldb/trunk/packages/Python/lldbsuite/test/lldbtest.py Thu Aug 30 10:29:37 2018
@@ -2145,6 +2145,46 @@ class TestBase(Base):
 
         return match_object
 
+
+    def complete_exactly(self, str_input, patterns):
+        self.complete_from_to(str_input, patterns, True)
+
+    def complete_from_to(self, str_input, patterns, turn_off_re_match=False):
+        """Test that the completion mechanism completes str_input to patterns,
+        where patterns could be a pattern-string or a list of pattern-strings"""
+        # Patterns should not be None in order to proceed.
+        self.assertFalse(patterns is None)
+        # And should be either a string or list of strings.  Check for list type
+        # below, if not, make a list out of the singleton string.  If patterns
+        # is not a string or not a list of strings, there'll be runtime errors
+        # later on.
+        if not isinstance(patterns, list):
+            patterns = [patterns]
+
+        interp = self.dbg.GetCommandInterpreter()
+        match_strings = lldb.SBStringList()
+        num_matches = interp.HandleCompletion(str_input, len(str_input), 0, -1, match_strings)
+        common_match = match_strings.GetStringAtIndex(0)
+        if num_matches == 0:
+            compare_string = str_input
+        else:
+            if common_match != None and len(common_match) > 0:
+                compare_string = str_input + common_match
+            else:
+                compare_string = ""
+                for idx in range(1, num_matches+1):
+                    compare_string += match_strings.GetStringAtIndex(idx) + "\n"
+
+        for p in patterns:
+            if turn_off_re_match:
+                self.expect(
+                    compare_string, msg=COMPLETION_MSG(
+                        str_input, p, match_strings), exe=False, substrs=[p])
+            else:
+                self.expect(
+                    compare_string, msg=COMPLETION_MSG(
+                        str_input, p, match_strings), exe=False, patterns=[p])
+
     def expect(
             self,
             str,

Modified: lldb/trunk/source/Commands/CommandObjectExpression.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectExpression.cpp?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectExpression.cpp (original)
+++ lldb/trunk/source/Commands/CommandObjectExpression.cpp Thu Aug 30 10:29:37 2018
@@ -307,6 +307,74 @@ CommandObjectExpression::~CommandObjectE
 
 Options *CommandObjectExpression::GetOptions() { return &m_option_group; }
 
+int CommandObjectExpression::HandleCompletion(CompletionRequest &request) {
+  EvaluateExpressionOptions options;
+  options.SetCoerceToId(m_varobj_options.use_objc);
+  options.SetLanguage(m_command_options.language);
+  options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever);
+  options.SetAutoApplyFixIts(false);
+  options.SetGenerateDebugInfo(false);
+
+  // We need a valid execution context with a frame pointer for this
+  // completion, so if we don't have one we should try to make a valid
+  // execution context.
+  if (m_interpreter.GetExecutionContext().GetFramePtr() == nullptr)
+    m_interpreter.UpdateExecutionContext(nullptr);
+
+  // This didn't work, so let's get out before we start doing things that
+  // expect a valid frame pointer.
+  if (m_interpreter.GetExecutionContext().GetFramePtr() == nullptr)
+    return 0;
+
+  ExecutionContext exe_ctx(m_interpreter.GetExecutionContext());
+
+  Target *target = exe_ctx.GetTargetPtr();
+
+  if (!target)
+    target = GetDummyTarget();
+
+  if (!target)
+    return 0;
+
+  unsigned cursor_pos = request.GetRawCursorPos();
+  llvm::StringRef code = request.GetRawLine();
+
+  const std::size_t original_code_size = code.size();
+
+  // Remove the first token which is 'expr' or some alias/abbreviation of that.
+  code = llvm::getToken(code).second.ltrim();
+  OptionsWithRaw args(code);
+  code = args.GetRawPart();
+
+  // The position where the expression starts in the command line.
+  assert(original_code_size >= code.size());
+  std::size_t raw_start = original_code_size - code.size();
+
+  // Check if the cursor is actually in the expression string, and if not, we
+  // exit.
+  // FIXME: We should complete the options here.
+  if (cursor_pos < raw_start)
+    return 0;
+
+  // Make the cursor_pos again relative to the start of the code string.
+  assert(cursor_pos >= raw_start);
+  cursor_pos -= raw_start;
+
+  auto language = exe_ctx.GetFrameRef().GetLanguage();
+
+  Status error;
+  lldb::UserExpressionSP expr(target->GetUserExpressionForLanguage(
+      code, llvm::StringRef(), language, UserExpression::eResultTypeAny,
+      options, error));
+  if (error.Fail())
+    return 0;
+
+  StringList matches;
+  expr->Complete(exe_ctx, matches, cursor_pos);
+  request.AddCompletions(matches);
+  return request.GetNumberOfMatches();
+}
+
 static lldb_private::Status
 CanBeUsedForElementCountPrinting(ValueObject &valobj) {
   CompilerType type(valobj.GetCompilerType());

Modified: lldb/trunk/source/Commands/CommandObjectExpression.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectExpression.h?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectExpression.h (original)
+++ lldb/trunk/source/Commands/CommandObjectExpression.h Thu Aug 30 10:29:37 2018
@@ -62,6 +62,8 @@ public:
 
   Options *GetOptions() override;
 
+  int HandleCompletion(CompletionRequest &request) override;
+
 protected:
   //------------------------------------------------------------------
   // IOHandler::Delegate functions

Modified: lldb/trunk/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp?rev=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp Thu Aug 30 10:29:37 2018
@@ -87,7 +87,8 @@ void ASTResultSynthesizer::TransformTopL
         SynthesizeObjCMethodResult(method_decl);
       }
     } else if (FunctionDecl *function_decl = dyn_cast<FunctionDecl>(D)) {
-      if (m_ast_context &&
+      // When completing user input the body of the function may be a nullptr.
+      if (m_ast_context && function_decl->hasBody() &&
           !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) {
         RecordPersistentTypes(function_decl);
         SynthesizeFunctionResult(function_decl);

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=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp Thu Aug 30 10:29:37 2018
@@ -34,6 +34,8 @@
 #include "clang/Parse/ParseAST.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaConsumer.h"
 
 #include "llvm/ADT/StringRef.h"
@@ -546,7 +548,248 @@ ClangExpressionParser::ClangExpressionPa
 
 ClangExpressionParser::~ClangExpressionParser() {}
 
+namespace {
+
+//----------------------------------------------------------------------
+/// @class CodeComplete
+///
+/// A code completion consumer for the clang Sema that is responsible for
+/// creating the completion suggestions when a user requests completion
+/// of an incomplete `expr` invocation.
+//----------------------------------------------------------------------
+class CodeComplete : public CodeCompleteConsumer {
+  CodeCompletionTUInfo CCTUInfo;
+
+  std::string expr;
+  unsigned position = 0;
+  StringList &matches;
+
+  /// Returns true if the given character can be used in an identifier.
+  /// This also returns true for numbers because for completion we usually
+  /// just iterate backwards over iterators.
+  ///
+  /// Note: lldb uses '$' in its internal identifiers, so we also allow this.
+  static bool IsIdChar(char c) {
+    return c == '_' || std::isalnum(c) || c == '$';
+  }
+
+  /// Returns true if the given character is used to separate arguments
+  /// in the command line of lldb.
+  static bool IsTokenSeparator(char c) { return c == ' ' || c == '\t'; }
+
+  /// Drops all tokens in front of the expression that are unrelated for
+  /// the completion of the cmd line. 'unrelated' means here that the token
+  /// is not interested for the lldb completion API result.
+  StringRef dropUnrelatedFrontTokens(StringRef cmd) {
+    if (cmd.empty())
+      return cmd;
+
+    // If we are at the start of a word, then all tokens are unrelated to
+    // the current completion logic.
+    if (IsTokenSeparator(cmd.back()))
+      return StringRef();
+
+    // Remove all previous tokens from the string as they are unrelated
+    // to completing the current token.
+    StringRef to_remove = cmd;
+    while (!to_remove.empty() && !IsTokenSeparator(to_remove.back())) {
+      to_remove = to_remove.drop_back();
+    }
+    cmd = cmd.drop_front(to_remove.size());
+
+    return cmd;
+  }
+
+  /// Removes the last identifier token from the given cmd line.
+  StringRef removeLastToken(StringRef cmd) {
+    while (!cmd.empty() && IsIdChar(cmd.back())) {
+      cmd = cmd.drop_back();
+    }
+    return cmd;
+  }
+
+  /// Attemps to merge the given completion from the given position into the
+  /// existing command. Returns the completion string that can be returned to
+  /// the lldb completion API.
+  std::string mergeCompletion(StringRef existing, unsigned pos,
+                              StringRef completion) {
+    StringRef existing_command = existing.substr(0, pos);
+    // We rewrite the last token with the completion, so let's drop that
+    // token from the command.
+    existing_command = removeLastToken(existing_command);
+    // We also should remove all previous tokens from the command as they
+    // would otherwise be added to the completion that already has the
+    // completion.
+    existing_command = dropUnrelatedFrontTokens(existing_command);
+    return existing_command.str() + completion.str();
+  }
+
+public:
+  /// Constructs a CodeComplete consumer that can be attached to a Sema.
+  /// @param[out] matches
+  ///    The list of matches that the lldb completion API expects as a result.
+  ///    This may already contain matches, so it's only allowed to append
+  ///    to this variable.
+  /// @param[out] expr
+  ///    The whole expression string that we are currently parsing. This
+  ///    string needs to be equal to the input the user typed, and NOT the
+  ///    final code that Clang is parsing.
+  /// @param[out] position
+  ///    The character position of the user cursor in the `expr` parameter.
+  ///
+  CodeComplete(StringList &matches, std::string expr, unsigned position)
+      : CodeCompleteConsumer(CodeCompleteOptions(), false),
+        CCTUInfo(std::make_shared<GlobalCodeCompletionAllocator>()), expr(expr),
+        position(position), matches(matches) {}
+
+  /// Deregisters and destroys this code-completion consumer.
+  virtual ~CodeComplete() {}
+
+  /// \name Code-completion filtering
+  /// Check if the result should be filtered out.
+  bool isResultFilteredOut(StringRef Filter,
+                           CodeCompletionResult Result) override {
+    // This code is mostly copied from CodeCompleteConsumer.
+    switch (Result.Kind) {
+    case CodeCompletionResult::RK_Declaration:
+      return !(
+          Result.Declaration->getIdentifier() &&
+          Result.Declaration->getIdentifier()->getName().startswith(Filter));
+    case CodeCompletionResult::RK_Keyword:
+      return !StringRef(Result.Keyword).startswith(Filter);
+    case CodeCompletionResult::RK_Macro:
+      return !Result.Macro->getName().startswith(Filter);
+    case CodeCompletionResult::RK_Pattern:
+      return !StringRef(Result.Pattern->getAsString()).startswith(Filter);
+    }
+    // If we trigger this assert or the above switch yields a warning, then
+    // CodeCompletionResult has been enhanced with more kinds of completion
+    // results. Expand the switch above in this case.
+    assert(false && "Unknown completion result type?");
+    // If we reach this, then we should just ignore whatever kind of unknown
+    // result we got back. We probably can't turn it into any kind of useful
+    // completion suggestion with the existing code.
+    return true;
+  }
+
+  /// \name Code-completion callbacks
+  /// Process the finalized code-completion results.
+  void ProcessCodeCompleteResults(Sema &SemaRef, CodeCompletionContext Context,
+                                  CodeCompletionResult *Results,
+                                  unsigned NumResults) override {
+
+    // The Sema put the incomplete token we try to complete in here during
+    // lexing, so we need to retrieve it here to know what we are completing.
+    StringRef Filter = SemaRef.getPreprocessor().getCodeCompletionFilter();
+
+    // Iterate over all the results. Filter out results we don't want and
+    // process the rest.
+    for (unsigned I = 0; I != NumResults; ++I) {
+      // Filter the results with the information from the Sema.
+      if (!Filter.empty() && isResultFilteredOut(Filter, Results[I]))
+        continue;
+
+      CodeCompletionResult &R = Results[I];
+      std::string ToInsert;
+      // Handle the different completion kinds that come from the Sema.
+      switch (R.Kind) {
+      case CodeCompletionResult::RK_Declaration: {
+        const NamedDecl *D = R.Declaration;
+        ToInsert = R.Declaration->getNameAsString();
+        // If we have a function decl that has no arguments we want to
+        // complete the empty parantheses for the user. If the function has
+        // arguments, we at least complete the opening bracket.
+        if (const FunctionDecl *F = dyn_cast<FunctionDecl>(D)) {
+          if (F->getNumParams() == 0)
+            ToInsert += "()";
+          else
+            ToInsert += "(";
+        }
+        // If we try to complete a namespace, then we directly can append
+        // the '::'.
+        if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) {
+          if (!N->isAnonymousNamespace())
+            ToInsert += "::";
+        }
+        break;
+      }
+      case CodeCompletionResult::RK_Keyword:
+        ToInsert = R.Keyword;
+        break;
+      case CodeCompletionResult::RK_Macro:
+        // It's not clear if we want to complete any macros in the
+        ToInsert = R.Macro->getName().str();
+        break;
+      case CodeCompletionResult::RK_Pattern:
+        ToInsert = R.Pattern->getTypedText();
+        break;
+      }
+      // At this point all information is in the ToInsert string.
+
+      // We also filter some internal lldb identifiers here. The user
+      // shouldn't see these.
+      if (StringRef(ToInsert).startswith("$__lldb_"))
+        continue;
+      if (!ToInsert.empty()) {
+        // Merge the suggested Token into the existing command line to comply
+        // with the kind of result the lldb API expects.
+        std::string CompletionSuggestion =
+            mergeCompletion(expr, position, ToInsert);
+        matches.AppendString(CompletionSuggestion);
+      }
+    }
+  }
+
+  /// \param S the semantic-analyzer object for which code-completion is being
+  /// done.
+  ///
+  /// \param CurrentArg the index of the current argument.
+  ///
+  /// \param Candidates an array of overload candidates.
+  ///
+  /// \param NumCandidates the number of overload candidates
+  void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
+                                 OverloadCandidate *Candidates,
+                                 unsigned NumCandidates,
+                                 SourceLocation OpenParLoc) override {
+    // At the moment we don't filter out any overloaded candidates.
+  }
+
+  CodeCompletionAllocator &getAllocator() override {
+    return CCTUInfo.getAllocator();
+  }
+
+  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
+};
+} // namespace
+
+bool ClangExpressionParser::Complete(StringList &matches, unsigned line,
+                                     unsigned pos, unsigned typed_pos) {
+  DiagnosticManager mgr;
+  // We need the raw user expression here because that's what the CodeComplete
+  // class uses to provide completion suggestions.
+  // However, the `Text` method only gives us the transformed expression here.
+  // To actually get the raw user input here, we have to cast our expression to
+  // the LLVMUserExpression which exposes the right API. This should never fail
+  // as we always have a ClangUserExpression whenever we call this.
+  LLVMUserExpression &llvm_expr = *static_cast<LLVMUserExpression *>(&m_expr);
+  CodeComplete CC(matches, llvm_expr.GetUserText(), typed_pos);
+  // We don't need a code generator for parsing.
+  m_code_generator.reset();
+  // Start parsing the expression with our custom code completion consumer.
+  ParseInternal(mgr, &CC, line, pos);
+  return true;
+}
+
 unsigned ClangExpressionParser::Parse(DiagnosticManager &diagnostic_manager) {
+  return ParseInternal(diagnostic_manager);
+}
+
+unsigned
+ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager,
+                                     CodeCompleteConsumer *completion_consumer,
+                                     unsigned completion_line,
+                                     unsigned completion_column) {
   ClangDiagnosticManagerAdapter *adapter =
       static_cast<ClangDiagnosticManagerAdapter *>(
           m_compiler->getDiagnostics().getClient());
@@ -559,8 +802,18 @@ unsigned ClangExpressionParser::Parse(Di
 
   clang::SourceManager &source_mgr = m_compiler->getSourceManager();
   bool created_main_file = false;
-  if (m_compiler->getCodeGenOpts().getDebugInfo() ==
-      codegenoptions::FullDebugInfo) {
+
+  // Clang wants to do completion on a real file known by Clang's file manager,
+  // so we have to create one to make this work.
+  // TODO: We probably could also simulate to Clang's file manager that there
+  // is a real file that contains our code.
+  bool should_create_file = completion_consumer != nullptr;
+
+  // We also want a real file on disk if we generate full debug info.
+  should_create_file |= m_compiler->getCodeGenOpts().getDebugInfo() ==
+                        codegenoptions::FullDebugInfo;
+
+  if (should_create_file) {
     int temp_fd = -1;
     llvm::SmallString<PATH_MAX> result_path;
     if (FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir()) {
@@ -605,14 +858,30 @@ unsigned ClangExpressionParser::Parse(Di
   if (ClangExpressionDeclMap *decl_map = type_system_helper->DeclMap())
     decl_map->InstallCodeGenerator(m_code_generator.get());
 
+  // If we want to parse for code completion, we need to attach our code
+  // completion consumer to the Sema and specify a completion position.
+  // While parsing the Sema will call this consumer with the provided
+  // completion suggestions.
+  if (completion_consumer) {
+    auto main_file = source_mgr.getFileEntryForID(source_mgr.getMainFileID());
+    auto &PP = m_compiler->getPreprocessor();
+    // Lines and columns start at 1 in Clang, but code completion positions are
+    // indexed from 0, so we need to add 1 to the line and column here.
+    ++completion_line;
+    ++completion_column;
+    PP.SetCodeCompletionPoint(main_file, completion_line, completion_column);
+  }
+
   if (ast_transformer) {
     ast_transformer->Initialize(m_compiler->getASTContext());
     ParseAST(m_compiler->getPreprocessor(), ast_transformer,
-             m_compiler->getASTContext());
+             m_compiler->getASTContext(), false, TU_Complete,
+             completion_consumer);
   } else {
     m_code_generator->Initialize(m_compiler->getASTContext());
     ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(),
-             m_compiler->getASTContext());
+             m_compiler->getASTContext(), false, TU_Complete,
+             completion_consumer);
   }
 
   diag_buf->EndSourceFile();

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=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h Thu Aug 30 10:29:37 2018
@@ -20,6 +20,10 @@
 #include <string>
 #include <vector>
 
+namespace clang {
+class CodeCompleteConsumer;
+}
+
 namespace lldb_private {
 
 class IRExecutionUnit;
@@ -58,6 +62,9 @@ public:
   //------------------------------------------------------------------
   ~ClangExpressionParser() override;
 
+  bool Complete(StringList &matches, unsigned line, unsigned pos,
+                unsigned typed_pos) override;
+
   //------------------------------------------------------------------
   /// Parse a single expression and convert it to IR using Clang.  Don't wrap
   /// the expression in anything at all.
@@ -143,6 +150,33 @@ public:
   std::string GetClangTargetABI(const ArchSpec &target_arch);
 
 private:
+  //------------------------------------------------------------------
+  /// Parses the expression.
+  ///
+  /// @param[in] diagnostic_manager
+  ///     The diagnostic manager that should receive the diagnostics
+  ///     from the parsing process.
+  ///
+  /// @param[in] completion
+  ///     The completion consumer that should be used during parsing
+  ///     (or a nullptr if no consumer should be attached).
+  ///
+  /// @param[in] completion_line
+  ///     The line in which the completion marker should be placed.
+  ///     The first line is represented by the value 0.
+  ///
+  /// @param[in] completion_column
+  ///     The column in which the completion marker should be placed.
+  ///     The first column is represented by the value 0.
+  ///
+  /// @return
+  ///    The number of parsing errors.
+  //-------------------------------------------------------------------
+  unsigned ParseInternal(DiagnosticManager &diagnostic_manager,
+                         clang::CodeCompleteConsumer *completion = nullptr,
+                         unsigned completion_line = 0,
+                         unsigned completion_column = 0);
+
   std::unique_ptr<llvm::LLVMContext>
       m_llvm_context; ///< The LLVM context to generate IR into
   std::unique_ptr<clang::FileManager>

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=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp Thu Aug 30 10:29:37 2018
@@ -402,6 +402,16 @@ llvm::Optional<lldb::LanguageType> Clang
                                    "couldn't construct expression body");
       return llvm::Optional<lldb::LanguageType>();
     }
+
+    // Find and store the start position of the original code inside the
+    // 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(
+        m_transformed_text, lang_type, original_start, original_end);
+    if (found_bounds) {
+      m_user_expression_start_pos = original_start;
+    }
   }
   return lang_type;
 }
@@ -591,6 +601,119 @@ bool ClangUserExpression::Parse(Diagnost
   return true;
 }
 
+//------------------------------------------------------------------
+/// Converts an absolute position inside a given code string into
+/// a column/line pair.
+///
+/// @param[in] abs_pos
+///     A absolute position in the code string that we want to convert
+///     to a column/line pair.
+///
+/// @param[in] code
+///     A multi-line string usually representing source code.
+///
+/// @param[out] line
+///     The line in the code that contains the given absolute position.
+///     The first line in the string is indexed as 1.
+///
+/// @param[out] column
+///     The column in the line that contains the absolute position.
+///     The first character in a line is indexed as 0.
+//------------------------------------------------------------------
+static void AbsPosToLineColumnPos(unsigned abs_pos, llvm::StringRef code,
+                                  unsigned &line, unsigned &column) {
+  // Reset to code position to beginning of the file.
+  line = 0;
+  column = 0;
+
+  assert(abs_pos <= code.size() && "Absolute position outside code string?");
+
+  // We have to walk up to the position and count lines/columns.
+  for (std::size_t i = 0; i < abs_pos; ++i) {
+    // If we hit a line break, we go back to column 0 and enter a new line.
+    // We only handle \n because that's what we internally use to make new
+    // lines for our temporary code strings.
+    if (code[i] == '\n') {
+      ++line;
+      column = 0;
+      continue;
+    }
+    ++column;
+  }
+}
+
+bool ClangUserExpression::Complete(ExecutionContext &exe_ctx,
+                                   StringList &matches, unsigned complete_pos) {
+  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
+
+  // We don't want any visible feedback when completing an expression. Mostly
+  // because the results we get from an incomplete invocation are probably not
+  // correct.
+  DiagnosticManager diagnostic_manager;
+
+  if (!PrepareForParsing(diagnostic_manager, exe_ctx))
+    return false;
+
+  lldb::LanguageType lang_type = lldb::LanguageType::eLanguageTypeUnknown;
+  if (auto new_lang = GetLanguageForExpr(diagnostic_manager, exe_ctx))
+    lang_type = new_lang.getValue();
+
+  if (log)
+    log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
+
+  //////////////////////////
+  // Parse the expression
+  //
+
+  m_materializer_ap.reset(new Materializer());
+
+  ResetDeclMap(exe_ctx, m_result_delegate, /*keep result in memory*/ true);
+
+  OnExit on_exit([this]() { ResetDeclMap(); });
+
+  if (!DeclMap()->WillParse(exe_ctx, m_materializer_ap.get())) {
+    diagnostic_manager.PutString(
+        eDiagnosticSeverityError,
+        "current process state is unsuitable for expression parsing");
+
+    return false;
+  }
+
+  if (m_options.GetExecutionPolicy() == eExecutionPolicyTopLevel) {
+    DeclMap()->SetLookupsEnabled(true);
+  }
+
+  Process *process = exe_ctx.GetProcessPtr();
+  ExecutionContextScope *exe_scope = process;
+
+  if (!exe_scope)
+    exe_scope = exe_ctx.GetTargetPtr();
+
+  ClangExpressionParser parser(exe_scope, *this, false);
+
+  // We have to find the source code location where the user text is inside
+  // the transformed expression code. When creating the transformed text, we
+  // already stored the absolute position in the m_transformed_text string. The
+  // only thing left to do is to transform it into the line:column format that
+  // Clang expects.
+
+  // The line and column of the user expression inside the transformed source
+  // code.
+  unsigned user_expr_line, user_expr_column;
+  if (m_user_expression_start_pos.hasValue())
+    AbsPosToLineColumnPos(*m_user_expression_start_pos, m_transformed_text,
+                          user_expr_line, user_expr_column);
+  else
+    return false;
+
+  // The actual column where we have to complete is the start column of the
+  // user expression + the offset inside the user code that we were given.
+  const unsigned completion_column = user_expr_column + complete_pos;
+  parser.Complete(matches, user_expr_line, completion_column, complete_pos);
+
+  return true;
+}
+
 bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx,
                                        std::vector<lldb::addr_t> &args,
                                        lldb::addr_t struct_address,

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=341086&r1=341085&r2=341086&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h (original)
+++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h Thu Aug 30 10:29:37 2018
@@ -143,6 +143,9 @@ public:
              lldb_private::ExecutionPolicy execution_policy,
              bool keep_result_in_memory, bool generate_debug_info) override;
 
+  bool Complete(ExecutionContext &exe_ctx, StringList &matches,
+                unsigned complete_pos) override;
+
   ExpressionTypeSystemHelper *GetTypeSystemHelper() override {
     return &m_type_system_helper;
   }
@@ -198,6 +201,10 @@ private:
     lldb::TargetSP m_target_sp;
   };
 
+  /// The absolute character position in the transformed source code where the
+  /// user code (as typed by the user) starts. If the variable is empty, then we
+  /// were not able to calculate this position.
+  llvm::Optional<unsigned> m_user_expression_start_pos;
   ResultDelegate m_result_delegate;
 };
 




More information about the lldb-commits mailing list