|
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"
|
@@ -693,6 +694,69 @@ SwiftUserExpression::GetTextAndSetExpressionParser(
|
693 | 694 | return parse_result;
|
694 | 695 | }
|
695 | 696 |
|
| 697 | +/// If `sc` represents a "closure-like" function according to `lang`, and |
| 698 | +/// `var_name` can be found in a parent context, create a diagnostic |
| 699 | +/// explaining that this variable is available but not captured by the closure. |
| 700 | +static std::string |
| 701 | +CreateVarInParentScopeDiagnostic(StringRef var_name, |
| 702 | + StringRef parent_func_name) { |
| 703 | + return llvm::formatv("Current frame is a closure.\nA variable named '{0}' " |
| 704 | + "exists in function '{1}', but it " |
| 705 | + "was not captured.\nHint: the variable may be available " |
| 706 | + "in a parent frame.", |
| 707 | + var_name, parent_func_name); |
| 708 | +} |
| 709 | + |
| 710 | +/// If `diagnostic_manager` contains a "cannot find <var_name> in scope" |
| 711 | +/// diagnostic, attempt to enhance it by showing if `var_name` is used inside a |
| 712 | +/// closure, not captured, but defined in a parent scope. |
| 713 | +static void EnhanceNotInScopeDiagnostics(DiagnosticManager &diagnostic_manager, |
| 714 | + ExecutionContextScope *exe_scope) { |
| 715 | + if (!exe_scope) |
| 716 | + return; |
| 717 | + lldb::StackFrameSP stack_frame = exe_scope->CalculateStackFrame(); |
| 718 | + if (!stack_frame) |
| 719 | + return; |
| 720 | + SymbolContext sc = |
| 721 | + stack_frame->GetSymbolContext(lldb::eSymbolContextEverything); |
| 722 | + Language *swift_lang = |
| 723 | + Language::FindPlugin(lldb::LanguageType::eLanguageTypeSwift); |
| 724 | + if (!swift_lang) |
| 725 | + return; |
| 726 | + |
| 727 | + static const RegularExpression not_in_scope_regex = |
| 728 | + RegularExpression("(.*): cannot find '([^']+)' in scope\n(.*)"); |
| 729 | + for (auto &diag : diagnostic_manager.Diagnostics()) { |
| 730 | + if (!diag) |
| 731 | + continue; |
| 732 | + |
| 733 | + llvm::SmallVector<StringRef, 4> match_groups; |
| 734 | + |
| 735 | + if (StringRef old_rendered_msg = diag->GetDetail().rendered; |
| 736 | + !not_in_scope_regex.Execute(old_rendered_msg, &match_groups)) |
| 737 | + continue; |
| 738 | + |
| 739 | + StringRef prefix = match_groups[1]; |
| 740 | + StringRef var_name = match_groups[2]; |
| 741 | + StringRef suffix = match_groups[3]; |
| 742 | + |
| 743 | + Function *parent_func = |
| 744 | + swift_lang->FindParentOfClosureWithVariable(var_name, sc); |
| 745 | + if (!parent_func) |
| 746 | + continue; |
| 747 | + std::string new_message = CreateVarInParentScopeDiagnostic( |
| 748 | + var_name, parent_func->GetDisplayName()); |
| 749 | + |
| 750 | + std::string new_rendered = |
| 751 | + llvm::formatv("{0}: {1}\n{2}", prefix, new_message, suffix); |
| 752 | + const DiagnosticDetail &old_detail = diag->GetDetail(); |
| 753 | + diag = std::make_unique<Diagnostic>( |
| 754 | + diag->getKind(), diag->GetCompilerID(), |
| 755 | + DiagnosticDetail{old_detail.source_location, old_detail.severity, |
| 756 | + std::move(new_message), std::move(new_rendered)}); |
| 757 | + } |
| 758 | +} |
| 759 | + |
696 | 760 | bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager,
|
697 | 761 | ExecutionContext &exe_ctx,
|
698 | 762 | lldb_private::ExecutionPolicy execution_policy,
|
@@ -888,6 +952,7 @@ bool SwiftUserExpression::Parse(DiagnosticManager &diagnostic_manager,
|
888 | 952 | fixed_expression.substr(fixed_start, fixed_end - fixed_start);
|
889 | 953 | }
|
890 | 954 | }
|
| 955 | + EnhanceNotInScopeDiagnostics(diagnostic_manager, exe_scope); |
891 | 956 | return false;
|
892 | 957 | case ParseResult::success:
|
893 | 958 | llvm_unreachable("Success case is checked separately before switch!");
|
|
0 commit comments