|
35 | 35 | #include "lldb/Symbol/Type.h" |
36 | 36 | #include "lldb/Symbol/Variable.h" |
37 | 37 | #include "lldb/Symbol/VariableList.h" |
| 38 | +#include "lldb/Target/Language.h" |
38 | 39 | #include "lldb/Utility/LLDBAssert.h" |
39 | 40 | #include "lldb/Utility/LLDBLog.h" |
40 | 41 | #include "lldb/Utility/Log.h" |
@@ -700,6 +701,68 @@ SwiftUserExpression::GetTextAndSetExpressionParser( |
700 | 701 | return parse_result; |
701 | 702 | } |
702 | 703 |
|
| 704 | +/// If `sc` represents a "closure-like" function according to `lang`, and |
| 705 | +/// `var_name` can be found in a parent context, create a diagnostic |
| 706 | +/// explaining that this variable is available but not captured by the closure. |
| 707 | +static std::string |
| 708 | +CreateVarInParentScopeDiagnostic(StringRef var_name, |
| 709 | + StringRef parent_func_name) { |
| 710 | + return llvm::formatv("A variable named '{0}' existed in function '{1}', but " |
| 711 | + "it was not captured in the closure.\nHint: the " |
| 712 | + "variable may be available in a parent frame.", |
| 713 | + var_name, parent_func_name); |
| 714 | +} |
| 715 | + |
| 716 | +/// If `diagnostic_manager` contains a "cannot find <var_name> in scope" |
| 717 | +/// diagnostic, attempt to enhance it by showing if `var_name` is used inside a |
| 718 | +/// closure, not captured, but defined in a parent scope. |
| 719 | +static void EnhanceNotInScopeDiagnostics(DiagnosticManager &diagnostic_manager, |
| 720 | + ExecutionContextScope *exe_scope) { |
| 721 | + if (!exe_scope) |
| 722 | + return; |
| 723 | + lldb::StackFrameSP stack_frame = exe_scope->CalculateStackFrame(); |
| 724 | + if (!stack_frame) |
| 725 | + return; |
| 726 | + SymbolContext sc = |
| 727 | + stack_frame->GetSymbolContext(lldb::eSymbolContextEverything); |
| 728 | + Language *swift_lang = |
| 729 | + Language::FindPlugin(lldb::LanguageType::eLanguageTypeSwift); |
| 730 | + if (!swift_lang) |
| 731 | + return; |
| 732 | + |
| 733 | + static const RegularExpression not_in_scope_regex = |
| 734 | + RegularExpression("(.*): cannot find '([^']+)' in scope\n(.*)"); |
| 735 | + for (auto &diag : diagnostic_manager.Diagnostics()) { |
| 736 | + if (!diag) |
| 737 | + continue; |
| 738 | + |
| 739 | + llvm::SmallVector<StringRef, 4> match_groups; |
| 740 | + |
| 741 | + if (StringRef old_rendered_msg = diag->GetDetail().rendered; |
| 742 | + !not_in_scope_regex.Execute(old_rendered_msg, &match_groups)) |
| 743 | + continue; |
| 744 | + |
| 745 | + StringRef prefix = match_groups[1]; |
| 746 | + StringRef var_name = match_groups[2]; |
| 747 | + StringRef suffix = match_groups[3]; |
| 748 | + |
| 749 | + Function *parent_func = |
| 750 | + swift_lang->FindParentOfClosureWithVariable(var_name, sc); |
| 751 | + if (!parent_func) |
| 752 | + continue; |
| 753 | + std::string new_message = CreateVarInParentScopeDiagnostic( |
| 754 | + var_name, parent_func->GetDisplayName()); |
| 755 | + |
| 756 | + std::string new_rendered = |
| 757 | + llvm::formatv("{0}: {1}\n{2}", prefix, new_message, suffix); |
| 758 | + const DiagnosticDetail &old_detail = diag->GetDetail(); |
| 759 | + diag = std::make_unique<Diagnostic>( |
| 760 | + diag->getKind(), diag->GetCompilerID(), |
| 761 | + DiagnosticDetail{old_detail.source_location, old_detail.severity, |
| 762 | + std::move(new_message), std::move(new_rendered)}); |
| 763 | + } |
| 764 | +} |
| 765 | + |
703 | 766 | bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager, |
704 | 767 | ExecutionContext &exe_ctx, |
705 | 768 | lldb_private::ExecutionPolicy execution_policy, |
@@ -895,6 +958,7 @@ bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager, |
895 | 958 | fixed_expression.substr(fixed_start, fixed_end - fixed_start); |
896 | 959 | } |
897 | 960 | } |
| 961 | + EnhanceNotInScopeDiagnostics(diagnostic_manager, exe_scope); |
898 | 962 | return false; |
899 | 963 | case ParseResult::success: |
900 | 964 | llvm_unreachable("Success case is checked separately before switch!"); |
|
0 commit comments