Index: lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py +++ lldb/trunk/packages/Python/lldbsuite/test/expression_command/completion/TestExprCompletion.py @@ -195,6 +195,39 @@ self.complete_exactly('expr some_expr.Self(). FooNoArgs', 'expr some_expr.Self(). FooNoArgsBar()') + @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24489") + def test_expr_completion_with_descriptions(self): + self.build() + self.main_source = "main.cpp" + self.main_source_spec = lldb.SBFileSpec(self.main_source) + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(self, + '// Break here', self.main_source_spec) + + self.check_completion_with_desc("expr ", [ + # VarDecls have their type as description. + ["some_expr", "Expr &"], + # builtin types have no description. + ["int", ""], + ["float", ""] + ]) + self.check_completion_with_desc("expr some_expr.", [ + # Functions have their signature as description. + ["some_expr.Self()", "Expr &Self()"], + ["some_expr.operator=(", "inline Expr &operator=(const Expr &)"], + ["some_expr.FooNumbersBar1()", "int FooNumbersBar1()"], + ["some_expr.StaticMemberMethodBar()", "static int StaticMemberMethodBar()"], + ["some_expr.FooWithArgsBar(", "int FooWithArgsBar(int)"], + ["some_expr.FooNoArgsBar()", "int FooNoArgsBar()"], + ["some_expr.FooUnderscoreBar_()", "int FooUnderscoreBar_()"], + ["some_expr.FooWithMultipleArgsBar(", "int FooWithMultipleArgsBar(int, int)"], + ["some_expr.~Expr()", "inline ~Expr()"], + # FieldDecls have their type as description. + ["some_expr.MemberVariableBar", "int"], + ]) + + def assume_no_completions(self, str_input, cursor_pos = None): interp = self.dbg.GetCommandInterpreter() match_strings = lldb.SBStringList() Index: lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp =================================================================== --- lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ lldb/trunk/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/AST/ExternalASTSource.h" +#include "clang/AST/PrettyPrinter.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceLocation.h" @@ -564,6 +565,9 @@ std::string m_expr; unsigned m_position = 0; CompletionRequest &m_request; + /// The printing policy we use when printing declarations for our completion + /// descriptions. + clang::PrintingPolicy m_desc_policy; /// Returns true if the given character can be used in an identifier. /// This also returns true for numbers because for completion we usually @@ -638,10 +642,22 @@ /// @param[out] position /// The character position of the user cursor in the `expr` parameter. /// - CodeComplete(CompletionRequest &request, std::string expr, unsigned position) + CodeComplete(CompletionRequest &request, clang::LangOptions ops, + std::string expr, unsigned position) : CodeCompleteConsumer(CodeCompleteOptions(), false), m_info(std::make_shared()), m_expr(expr), - m_position(position), m_request(request) {} + m_position(position), m_request(request), m_desc_policy(ops) { + + // Ensure that the printing policy is producing a description that is as + // short as possible. + m_desc_policy.SuppressScope = true; + m_desc_policy.SuppressTagKeyword = true; + m_desc_policy.FullyQualifiedName = false; + m_desc_policy.TerseOutput = true; + m_desc_policy.IncludeNewlines = false; + m_desc_policy.UseVoidForZeroParams = false; + m_desc_policy.Bool = true; + } /// Deregisters and destroys this code-completion consumer. virtual ~CodeComplete() {} @@ -692,6 +708,7 @@ CodeCompletionResult &R = Results[I]; std::string ToInsert; + std::string Description; // Handle the different completion kinds that come from the Sema. switch (R.Kind) { case CodeCompletionResult::RK_Declaration: { @@ -705,10 +722,16 @@ ToInsert += "()"; else ToInsert += "("; - } - // If we try to complete a namespace, then we can directly append - // the '::'. - if (const NamespaceDecl *N = dyn_cast(D)) { + raw_string_ostream OS(Description); + F->print(OS, m_desc_policy, false); + OS.flush(); + } else if (const VarDecl *V = dyn_cast(D)) { + Description = V->getType().getAsString(m_desc_policy); + } else if (const FieldDecl *F = dyn_cast(D)) { + Description = F->getType().getAsString(m_desc_policy); + } else if (const NamespaceDecl *N = dyn_cast(D)) { + // If we try to complete a namespace, then we can directly append + // the '::'. if (!N->isAnonymousNamespace()) ToInsert += "::"; } @@ -735,7 +758,7 @@ // with the kind of result the lldb API expects. std::string CompletionSuggestion = mergeCompletion(m_expr, m_position, ToInsert); - m_request.AddCompletion(CompletionSuggestion); + m_request.AddCompletion(CompletionSuggestion, Description); } } } @@ -773,7 +796,8 @@ // 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(&m_expr); - CodeComplete CC(request, llvm_expr.GetUserText(), typed_pos); + CodeComplete CC(request, m_compiler->getLangOpts(), 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.