diff options
Diffstat (limited to 'lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp')
-rw-r--r-- | lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp | 357 |
1 files changed, 226 insertions, 131 deletions
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 8abd14942885..6ff028cf6980 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -1,4 +1,4 @@ -//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// +//===-- ClangExpressionParser.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -67,6 +67,7 @@ #include "IRForTarget.h" #include "ModuleDependencyCollector.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" @@ -75,7 +76,6 @@ #include "lldb/Expression/IRInterpreter.h" #include "lldb/Host/File.h" #include "lldb/Host/HostInfo.h" -#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Language.h" @@ -91,6 +91,7 @@ #include "lldb/Utility/StringList.h" #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" +#include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" #include <cctype> #include <memory> @@ -146,21 +147,40 @@ public: llvm::StringRef getErrorString() { return m_error_stream.GetString(); } }; +static void AddAllFixIts(ClangDiagnostic *diag, const clang::Diagnostic &Info) { + for (auto &fix_it : Info.getFixItHints()) { + if (fix_it.isNull()) + continue; + diag->AddFixitHint(fix_it); + } +} + class ClangDiagnosticManagerAdapter : public clang::DiagnosticConsumer { public: 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)); + DiagnosticOptions *options = new DiagnosticOptions(opts); + options->ShowPresumedLoc = true; + options->ShowLevel = false; + m_os = std::make_shared<llvm::raw_string_ostream>(m_output); + m_passthrough = + std::make_shared<clang::TextDiagnosticPrinter>(*m_os, options); } void ResetManager(DiagnosticManager *manager = nullptr) { m_manager = manager; } + /// Returns the last ClangDiagnostic message that the DiagnosticManager + /// received or a nullptr if the DiagnosticMangager hasn't seen any + /// Clang diagnostics yet. + ClangDiagnostic *MaybeGetLastClangDiag() const { + if (m_manager->Diagnostics().empty()) + return nullptr; + lldb_private::Diagnostic *diag = m_manager->Diagnostics().back().get(); + ClangDiagnostic *clang_diag = dyn_cast<ClangDiagnostic>(diag); + return clang_diag; + } + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override { if (!m_manager) { @@ -180,6 +200,9 @@ public: return; } + // Update error/warning counters. + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + // Render diagnostic message to m_output. m_output.clear(); m_passthrough->HandleDiagnostic(DiagLevel, Info); @@ -203,11 +226,29 @@ public: case DiagnosticsEngine::Level::Note: m_manager->AppendMessageToDiagnostic(m_output); make_new_diagnostic = false; + + // 'note:' diagnostics for errors and warnings can also contain Fix-Its. + // We add these Fix-Its to the last error diagnostic to make sure + // that we later have all Fix-Its related to an 'error' diagnostic when + // we apply them to the user expression. + auto *clang_diag = MaybeGetLastClangDiag(); + // If we don't have a previous diagnostic there is nothing to do. + // If the previous diagnostic already has its own Fix-Its, assume that + // the 'note:' Fix-It is just an alternative way to solve the issue and + // ignore these Fix-Its. + if (!clang_diag || clang_diag->HasFixIts()) + break; + // Ignore all Fix-Its that are not associated with an error. + if (clang_diag->GetSeverity() != eDiagnosticSeverityError) + break; + AddAllFixIts(clang_diag, Info); + break; } if (make_new_diagnostic) { // ClangDiagnostic messages are expected to have no whitespace/newlines // around them. - std::string stripped_output = llvm::StringRef(m_output).trim(); + std::string stripped_output = + std::string(llvm::StringRef(m_output).trim()); auto new_diagnostic = std::make_unique<ClangDiagnostic>( stripped_output, severity, Info.getID()); @@ -216,20 +257,18 @@ public: // enough context in an expression for the warning to be useful. // FIXME: Should we try to filter out FixIts that apply to our generated // code, and not the user's expression? - if (severity == eDiagnosticSeverityError) { - size_t num_fixit_hints = Info.getNumFixItHints(); - for (size_t i = 0; i < num_fixit_hints; i++) { - const clang::FixItHint &fixit = Info.getFixItHint(i); - if (!fixit.isNull()) - new_diagnostic->AddFixitHint(fixit); - } - } + if (severity == eDiagnosticSeverityError) + AddAllFixIts(new_diagnostic.get(), Info); m_manager->AddDiagnostic(std::move(new_diagnostic)); } } - clang::TextDiagnosticPrinter *GetPassthrough() { return m_passthrough.get(); } + void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override { + m_passthrough->BeginSourceFile(LO, PP); + } + + void EndSourceFile() override { m_passthrough->EndSourceFile(); } private: DiagnosticManager *m_manager = nullptr; @@ -253,9 +292,9 @@ static void SetupModuleHeaderPaths(CompilerInstance *compiler, } llvm::SmallString<128> module_cache; - auto props = ModuleList::GetGlobalModuleListProperties(); + const auto &props = ModuleList::GetGlobalModuleListProperties(); props.GetClangModulesCachePath().GetPath(module_cache); - search_opts.ModuleCachePath = module_cache.str(); + search_opts.ModuleCachePath = std::string(module_cache.str()); LLDB_LOG(log, "Using module cache path: {0}", module_cache.c_str()); search_opts.ResourceDir = GetClangResourceDir().GetPath(); @@ -279,28 +318,27 @@ ClangExpressionParser::ClangExpressionParser( // We can't compile expressions without a target. So if the exe_scope is // null or doesn't have a target, then we just need to get out of here. I'll - // lldb_assert and not make any of the compiler objects since + // lldbassert and not make any of the compiler objects since // I can't return errors directly from the constructor. Further calls will // check if the compiler was made and // bag out if it wasn't. if (!exe_scope) { - lldb_assert(exe_scope, "Can't make an expression parser with a null scope.", - __FUNCTION__, __FILE__, __LINE__); + lldbassert(exe_scope && + "Can't make an expression parser with a null scope."); return; } lldb::TargetSP target_sp; target_sp = exe_scope->CalculateTarget(); if (!target_sp) { - lldb_assert(target_sp.get(), - "Can't make an expression parser with a null target.", - __FUNCTION__, __FILE__, __LINE__); + lldbassert(target_sp.get() && + "Can't make an expression parser with a null target."); return; } // 1. Create a new compiler instance. - m_compiler.reset(new CompilerInstance()); + m_compiler = std::make_unique<CompilerInstance>(); // When capturing a reproducer, hook up the file collector with clang to // collector modules and headers. @@ -391,9 +429,13 @@ ClangExpressionParser::ClangExpressionParser( // target. In this case, a specialized language runtime is available and we // can query it for extra options. For 99% of use cases, this will not be // needed and should be provided when basic platform detection is not enough. - if (lang_rt) + // FIXME: Generalize this. Only RenderScriptRuntime currently supports this + // currently. Hardcoding this isn't ideal but it's better than LanguageRuntime + // having knowledge of clang::TargetOpts. + if (auto *renderscript_rt = + llvm::dyn_cast_or_null<RenderScriptRuntime>(lang_rt)) overridden_target_opts = - lang_rt->GetOverrideExprOptions(m_compiler->getTargetOpts()); + renderscript_rt->GetOverrideExprOptions(m_compiler->getTargetOpts()); if (overridden_target_opts) if (log && log->GetVerbose()) { @@ -606,11 +648,12 @@ ClangExpressionParser::ClangExpressionParser( m_compiler->createASTContext(); clang::ASTContext &ast_context = m_compiler->getASTContext(); - m_ast_context.reset(new ClangASTContext(ast_context)); + m_ast_context = std::make_unique<TypeSystemClang>( + "Expression ASTContext for '" + m_filename + "'", ast_context); std::string module_name("$__lldb_module"); - m_llvm_context.reset(new LLVMContext()); + m_llvm_context = std::make_unique<LLVMContext>(); m_code_generator.reset(CreateLLVMCodeGen( m_compiler->getDiagnostics(), module_name, m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(), @@ -631,11 +674,33 @@ class CodeComplete : public CodeCompleteConsumer { 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; + struct CompletionWithPriority { + CompletionResult::Completion completion; + /// See CodeCompletionResult::Priority; + unsigned Priority; + + /// Establishes a deterministic order in a list of CompletionWithPriority. + /// The order returned here is the order in which the completions are + /// displayed to the user. + bool operator<(const CompletionWithPriority &o) const { + // High priority results should come first. + if (Priority != o.Priority) + return Priority > o.Priority; + + // Identical priority, so just make sure it's a deterministic order. + return completion.GetUniqueKey() < o.completion.GetUniqueKey(); + } + }; + + /// The stored completions. + /// Warning: These are in a non-deterministic order until they are sorted + /// and returned back to the caller. + std::vector<CompletionWithPriority> m_completions; + /// 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. @@ -652,7 +717,7 @@ class CodeComplete : public CodeCompleteConsumer { /// 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) { + StringRef dropUnrelatedFrontTokens(StringRef cmd) const { if (cmd.empty()) return cmd; @@ -673,18 +738,18 @@ class CodeComplete : public CodeCompleteConsumer { } /// Removes the last identifier token from the given cmd line. - StringRef removeLastToken(StringRef cmd) { + StringRef removeLastToken(StringRef cmd) const { while (!cmd.empty() && IsIdChar(cmd.back())) { cmd = cmd.drop_back(); } return cmd; } - /// Attemps to merge the given completion from the given position into the + /// Attempts 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 completion) const { StringRef existing_command = existing.substr(0, pos); // We rewrite the last token with the completion, so let's drop that // token from the command. @@ -706,11 +771,10 @@ public: /// \param[out] position /// The character position of the user cursor in the `expr` parameter. /// - CodeComplete(CompletionRequest &request, clang::LangOptions ops, - std::string expr, unsigned position) + CodeComplete(clang::LangOptions ops, std::string expr, unsigned position) : CodeCompleteConsumer(CodeCompleteOptions()), m_info(std::make_shared<GlobalCodeCompletionAllocator>()), m_expr(expr), - m_position(position), m_request(request), m_desc_policy(ops) { + m_position(position), m_desc_policy(ops) { // Ensure that the printing policy is producing a description that is as // short as possible. @@ -723,9 +787,6 @@ public: m_desc_policy.Bool = true; } - /// Deregisters and destroys this code-completion consumer. - ~CodeComplete() override {} - /// \name Code-completion filtering /// Check if the result should be filtered out. bool isResultFilteredOut(StringRef Filter, @@ -753,6 +814,85 @@ public: return true; } +private: + /// Generate the completion strings for the given CodeCompletionResult. + /// Note that this function has to process results that could come in + /// non-deterministic order, so this function should have no side effects. + /// To make this easier to enforce, this function and all its parameters + /// should always be const-qualified. + /// \return Returns llvm::None if no completion should be provided for the + /// given CodeCompletionResult. + llvm::Optional<CompletionWithPriority> + getCompletionForResult(const CodeCompletionResult &R) const { + std::string ToInsert; + std::string Description; + // 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 += "("; + raw_string_ostream OS(Description); + F->print(OS, m_desc_policy, false); + OS.flush(); + } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) { + Description = V->getType().getAsString(m_desc_policy); + } else if (const FieldDecl *F = dyn_cast<FieldDecl>(D)) { + Description = F->getType().getAsString(m_desc_policy); + } else if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) { + // If we try to complete a namespace, then we can directly append + // the '::'. + if (!N->isAnonymousNamespace()) + ToInsert += "::"; + } + break; + } + case CodeCompletionResult::RK_Keyword: + ToInsert = R.Keyword; + break; + case CodeCompletionResult::RK_Macro: + ToInsert = R.Macro->getName().str(); + break; + case CodeCompletionResult::RK_Pattern: + ToInsert = R.Pattern->getTypedText(); + break; + } + // We also filter some internal lldb identifiers here. The user + // shouldn't see these. + if (llvm::StringRef(ToInsert).startswith("$__lldb_")) + return llvm::None; + if (ToInsert.empty()) + return llvm::None; + // Merge the suggested Token into the existing command line to comply + // with the kind of result the lldb API expects. + std::string CompletionSuggestion = + mergeCompletion(m_expr, m_position, ToInsert); + + CompletionResult::Completion completion(CompletionSuggestion, Description, + CompletionMode::Normal); + return {{completion, R.Priority}}; + } + +public: + /// Adds the completions to the given CompletionRequest. + void GetCompletions(CompletionRequest &request) { + // Bring m_completions into a deterministic order and pass it on to the + // CompletionRequest. + llvm::sort(m_completions); + + for (const CompletionWithPriority &C : m_completions) + request.AddCompletion(C.completion.GetCompletion(), + C.completion.GetDescription(), + C.completion.GetMode()); + } + /// \name Code-completion callbacks /// Process the finalized code-completion results. void ProcessCodeCompleteResults(Sema &SemaRef, CodeCompletionContext Context, @@ -771,59 +911,11 @@ public: continue; 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: { - 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 += "("; - raw_string_ostream OS(Description); - F->print(OS, m_desc_policy, false); - OS.flush(); - } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) { - Description = V->getType().getAsString(m_desc_policy); - } else if (const FieldDecl *F = dyn_cast<FieldDecl>(D)) { - Description = F->getType().getAsString(m_desc_policy); - } else if (const NamespaceDecl *N = dyn_cast<NamespaceDecl>(D)) { - // If we try to complete a namespace, then we can directly append - // the '::'. - if (!N->isAnonymousNamespace()) - ToInsert += "::"; - } - break; - } - case CodeCompletionResult::RK_Keyword: - ToInsert = R.Keyword; - break; - case CodeCompletionResult::RK_Macro: - 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_")) + llvm::Optional<CompletionWithPriority> CompletionAndPriority = + getCompletionForResult(R); + if (!CompletionAndPriority) 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(m_expr, m_position, ToInsert); - m_request.AddCompletion(CompletionSuggestion, Description); - } + m_completions.push_back(*CompletionAndPriority); } } @@ -860,12 +952,13 @@ bool ClangExpressionParser::Complete(CompletionRequest &request, unsigned line, // the LLVMUserExpression which exposes the right API. This should never fail // as we always have a ClangUserExpression whenever we call this. ClangUserExpression *llvm_expr = cast<ClangUserExpression>(&m_expr); - CodeComplete CC(request, m_compiler->getLangOpts(), llvm_expr->GetUserText(), + CodeComplete CC(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. ParseInternal(mgr, &CC, line, pos); + CC.GetCompletions(request); return true; } @@ -881,7 +974,6 @@ ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, ClangDiagnosticManagerAdapter *adapter = static_cast<ClangDiagnosticManagerAdapter *>( m_compiler->getDiagnostics().getClient()); - auto diag_buf = adapter->GetPassthrough(); adapter->ResetManager(&diagnostic_manager); @@ -936,8 +1028,8 @@ ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, source_mgr.setMainFileID(source_mgr.createFileID(std::move(memory_buffer))); } - diag_buf->BeginSourceFile(m_compiler->getLangOpts(), - &m_compiler->getPreprocessor()); + adapter->BeginSourceFile(m_compiler->getLangOpts(), + &m_compiler->getPreprocessor()); ClangExpressionHelper *type_system_helper = dyn_cast<ClangExpressionHelper>(m_expr.GetTypeSystemHelper()); @@ -961,11 +1053,11 @@ ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, std::unique_ptr<clang::ASTConsumer> Consumer; if (ast_transformer) { - Consumer.reset(new ASTConsumerForwarder(ast_transformer)); + Consumer = std::make_unique<ASTConsumerForwarder>(ast_transformer); } else if (m_code_generator) { - Consumer.reset(new ASTConsumerForwarder(m_code_generator.get())); + Consumer = std::make_unique<ASTConsumerForwarder>(m_code_generator.get()); } else { - Consumer.reset(new ASTConsumer()); + Consumer = std::make_unique<ASTConsumer>(); } clang::ASTContext &ast_context = m_compiler->getASTContext(); @@ -1022,9 +1114,9 @@ ClangExpressionParser::ParseInternal(DiagnosticManager &diagnostic_manager, // original behavior of ParseAST (which also destroys the Sema after parsing). m_compiler->setSema(nullptr); - diag_buf->EndSourceFile(); + adapter->EndSourceFile(); - unsigned num_errors = diag_buf->getNumErrors(); + unsigned num_errors = adapter->getNumErrors(); if (m_pp_callbacks && m_pp_callbacks->hasErrors()) { num_errors++; @@ -1065,6 +1157,28 @@ ClangExpressionParser::GetClangTargetABI(const ArchSpec &target_arch) { return abi; } +/// Applies the given Fix-It hint to the given commit. +static void ApplyFixIt(const FixItHint &fixit, clang::edit::Commit &commit) { + // This is cobbed from clang::Rewrite::FixItRewriter. + if (fixit.CodeToInsert.empty()) { + if (fixit.InsertFromRange.isValid()) { + commit.insertFromRange(fixit.RemoveRange.getBegin(), + fixit.InsertFromRange, /*afterToken=*/false, + fixit.BeforePreviousInsertions); + return; + } + commit.remove(fixit.RemoveRange); + return; + } + if (fixit.RemoveRange.isTokenRange() || + fixit.RemoveRange.getBegin() != fixit.RemoveRange.getEnd()) { + commit.replace(fixit.RemoveRange, fixit.CodeToInsert); + return; + } + commit.insert(fixit.RemoveRange.getBegin(), fixit.CodeToInsert, + /*afterToken=*/false, fixit.BeforePreviousInsertions); +} + bool ClangExpressionParser::RewriteExpression( DiagnosticManager &diagnostic_manager) { clang::SourceManager &source_manager = m_compiler->getSourceManager(); @@ -1096,26 +1210,12 @@ bool ClangExpressionParser::RewriteExpression( for (const auto &diag : diagnostic_manager.Diagnostics()) { const auto *diagnostic = llvm::dyn_cast<ClangDiagnostic>(diag.get()); - if (diagnostic && diagnostic->HasFixIts()) { - for (const FixItHint &fixit : diagnostic->FixIts()) { - // This is cobbed from clang::Rewrite::FixItRewriter. - if (fixit.CodeToInsert.empty()) { - if (fixit.InsertFromRange.isValid()) { - commit.insertFromRange(fixit.RemoveRange.getBegin(), - fixit.InsertFromRange, /*afterToken=*/false, - fixit.BeforePreviousInsertions); - } else - commit.remove(fixit.RemoveRange); - } else { - if (fixit.RemoveRange.isTokenRange() || - fixit.RemoveRange.getBegin() != fixit.RemoveRange.getEnd()) - commit.replace(fixit.RemoveRange, fixit.CodeToInsert); - else - commit.insert(fixit.RemoveRange.getBegin(), fixit.CodeToInsert, - /*afterToken=*/false, fixit.BeforePreviousInsertions); - } - } - } + if (!diagnostic) + continue; + if (!diagnostic->HasFixIts()) + continue; + for (const FixItHint &fixit : diagnostic->FixIts()) + ApplyFixIt(fixit, commit); } // FIXME - do we want to try to propagate specific errors here? @@ -1230,18 +1330,13 @@ lldb_private::Status ClangExpressionParser::PrepareForExecution( type_system_helper->DeclMap(); // result can be NULL if (decl_map) { - Target *target = exe_ctx.GetTargetPtr(); - auto &error_stream = target->GetDebugger().GetErrorStream(); + StreamString error_stream; IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(), *execution_unit_sp, error_stream, function_name.AsCString()); - bool ir_can_run = - ir_for_target.runOnModule(*execution_unit_sp->GetModule()); - - if (!ir_can_run) { - err.SetErrorString( - "The expression could not be prepared to run in the target"); + if (!ir_for_target.runOnModule(*execution_unit_sp->GetModule())) { + err.SetErrorString(error_stream.GetString()); return err; } |