Index: lldb/bindings/interface/SBExpressionOptions.i =================================================================== --- lldb/bindings/interface/SBExpressionOptions.i +++ lldb/bindings/interface/SBExpressionOptions.i @@ -126,6 +126,14 @@ bool GetAutoApplyFixIts(); + %feature("docstring", "Sets how often LLDB should retry applying fix-its to an expression.") SetRetriesWithFixIts; + void + SetRetriesWithFixIts(uint64_t retries); + + %feature("docstring", "Gets how often LLDB will retry applying fix-its to an expression.") GetRetriesWithFixIts; + uint64_t + GetRetriesWithFixIts(); + bool GetTopLevel(); Index: lldb/include/lldb/API/SBExpressionOptions.h =================================================================== --- lldb/include/lldb/API/SBExpressionOptions.h +++ lldb/include/lldb/API/SBExpressionOptions.h @@ -86,6 +86,10 @@ bool GetAutoApplyFixIts(); + void SetRetriesWithFixIts(uint64_t retries); + + uint64_t GetRetriesWithFixIts(); + bool GetTopLevel(); void SetTopLevel(bool b = true); Index: lldb/include/lldb/Target/Target.h =================================================================== --- lldb/include/lldb/Target/Target.h +++ lldb/include/lldb/Target/Target.h @@ -135,6 +135,8 @@ bool GetEnableAutoApplyFixIts() const; + uint64_t GetNumberOfRetriesWithFixits() const; + bool GetEnableNotifyAboutFixIts() const; bool GetEnableSaveObjects() const; @@ -380,6 +382,12 @@ bool GetAutoApplyFixIts() const { return m_auto_apply_fixits; } + void SetRetriesWithFixIts(uint64_t number_of_retries) { + m_retries_with_fixits = number_of_retries; + } + + uint64_t GetRetriesWithFixIts() const { return m_retries_with_fixits; } + bool IsForUtilityExpr() const { return m_running_utility_expression; } void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; } @@ -401,6 +409,7 @@ bool m_ansi_color_errors = false; bool m_result_is_internal = false; bool m_auto_apply_fixits = true; + uint64_t m_retries_with_fixits = 1; /// True if the executed code should be treated as utility code that is only /// used by LLDB internally. bool m_running_utility_expression = false; Index: lldb/source/API/SBExpressionOptions.cpp =================================================================== --- lldb/source/API/SBExpressionOptions.cpp +++ lldb/source/API/SBExpressionOptions.cpp @@ -237,6 +237,20 @@ return m_opaque_up->SetAutoApplyFixIts(b); } +uint64_t SBExpressionOptions::GetRetriesWithFixIts() { + LLDB_RECORD_METHOD_NO_ARGS(uint64_t, SBExpressionOptions, + GetRetriesWithFixIts); + + return m_opaque_up->GetRetriesWithFixIts(); +} + +void SBExpressionOptions::SetRetriesWithFixIts(uint64_t retries) { + LLDB_RECORD_METHOD(void, SBExpressionOptions, SetRetriesWithFixIts, + (uint64_t), retries); + + return m_opaque_up->SetRetriesWithFixIts(retries); +} + bool SBExpressionOptions::GetTopLevel() { LLDB_RECORD_METHOD_NO_ARGS(bool, SBExpressionOptions, GetTopLevel); Index: lldb/source/Commands/CommandObjectExpression.cpp =================================================================== --- lldb/source/Commands/CommandObjectExpression.cpp +++ lldb/source/Commands/CommandObjectExpression.cpp @@ -385,6 +385,7 @@ auto_apply_fixits = m_command_options.auto_apply_fixits == eLazyBoolYes; options.SetAutoApplyFixIts(auto_apply_fixits); + options.SetRetriesWithFixIts(target.GetNumberOfRetriesWithFixits()); if (m_command_options.top_level) options.SetExecutionPolicy(eExecutionPolicyTopLevel); Index: lldb/source/Expression/UserExpression.cpp =================================================================== --- lldb/source/Expression/UserExpression.cpp +++ lldb/source/Expression/UserExpression.cpp @@ -266,22 +266,33 @@ execution_results = lldb::eExpressionParseError; if (fixed_expression && !fixed_expression->empty() && options.GetAutoApplyFixIts()) { - lldb::UserExpressionSP fixed_expression_sp( - target->GetUserExpressionForLanguage(fixed_expression->c_str(), - full_prefix, language, - desired_type, options, ctx_obj, - error)); - DiagnosticManager fixed_diagnostic_manager; - parse_success = fixed_expression_sp->Parse( - fixed_diagnostic_manager, exe_ctx, execution_policy, - keep_expression_in_memory, generate_debug_info); - if (parse_success) { - diagnostic_manager.Clear(); - user_expression_sp = fixed_expression_sp; - } else { - // If the fixed expression failed to parse, don't tell the user about, - // that won't help. - fixed_expression->clear(); + const uint64_t max_fix_retries = options.GetRetriesWithFixIts(); + for (uint64_t i = 0; i < max_fix_retries; ++i) { + // Try parsing the fixed expression. + lldb::UserExpressionSP fixed_expression_sp( + target->GetUserExpressionForLanguage( + fixed_expression->c_str(), full_prefix, language, desired_type, + options, ctx_obj, error)); + DiagnosticManager fixed_diagnostic_manager; + parse_success = fixed_expression_sp->Parse( + fixed_diagnostic_manager, exe_ctx, execution_policy, + keep_expression_in_memory, generate_debug_info); + if (parse_success) { + diagnostic_manager.Clear(); + user_expression_sp = fixed_expression_sp; + break; + } else { + // The fixed expression also didn't parse. Let's check for any new + // Fix-Its we could try. + if (fixed_expression_sp->GetFixedText()) { + *fixed_expression = fixed_expression_sp->GetFixedText(); + } else { + // Fixed expression didn't compile without a fixit, don't retry and + // don't tell the user about it. + fixed_expression->clear(); + break; + } + } } } Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -3712,6 +3712,12 @@ nullptr, idx, g_target_properties[idx].default_uint_value != 0); } +uint64_t TargetProperties::GetNumberOfRetriesWithFixits() const { + const uint32_t idx = ePropertyRetriesWithFixIts; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + bool TargetProperties::GetEnableNotifyAboutFixIts() const { const uint32_t idx = ePropertyNotifyAboutFixIts; return m_collection_sp->GetPropertyAtIndexAsBoolean( Index: lldb/source/Target/TargetProperties.td =================================================================== --- lldb/source/Target/TargetProperties.td +++ lldb/source/Target/TargetProperties.td @@ -51,6 +51,9 @@ def AutoApplyFixIts: Property<"auto-apply-fixits", "Boolean">, DefaultTrue, Desc<"Automatically apply fix-it hints to expressions.">; + def RetriesWithFixIts: Property<"retries-with-fixits", "UInt64">, + DefaultUnsignedValue<1>, + Desc<"Maximum number of attempts to fix an expression with Fix-Its">; def NotifyAboutFixIts: Property<"notify-about-fixits", "Boolean">, DefaultTrue, Desc<"Print the fixed expression text.">; Index: lldb/test/API/commands/expression/fixits/TestFixIts.py =================================================================== --- lldb/test/API/commands/expression/fixits/TestFixIts.py +++ lldb/test/API/commands/expression/fixits/TestFixIts.py @@ -81,3 +81,71 @@ self.assertTrue( error_string.find("my_pointer->second.a") != -1, "Fix was right") + + + # Test repeatedly applying Fix-Its to expressions and reparsing them. + multiple_runs_options = lldb.SBExpressionOptions() + multiple_runs_options.SetAutoApplyFixIts(True) + multiple_runs_options.SetTopLevel(True) + + # An expression that needs two parse attempts with one Fix-It each + # to be successfully parsed. + two_runs_expr = """ + struct Data { int m; }; + + template + struct S1 : public T { + using T::TypeDef; + int f() { + Data d; + d.m = 123; + // The first error as the using above requires a 'typename '. + // Will trigger a Fix-It that puts 'typename' in the right place. + typename S1::TypeDef i = &d; + // i has the type "Data *", so this should be i.m. + // The second run will change the . to -> via the Fix-It. + return i.m; + } + }; + + struct ClassWithTypeDef { + typedef Data *TypeDef; + }; + + int test_X(int i) { + S1 s1; + return s1.f(); + } + """ + + # Disable retries which will fail. + multiple_runs_options.SetRetriesWithFixIts(0) + value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options) + self.assertIn("expression failed to parse, fixed expression suggested:", + value.GetError().GetCString()) + self.assertIn("using typename T::TypeDef", + value.GetError().GetCString()) + # The second Fix-It shouldn't be suggested here as Clang should have + # aborted the parsing process. + self.assertNotIn("i->m", + value.GetError().GetCString()) + + # Retry once, but the expression needs two retries. + multiple_runs_options.SetRetriesWithFixIts(1) + value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options) + self.assertIn("expression failed to parse, fixed expression suggested:", + value.GetError().GetCString()) + # Both our fixed expressions should be in the suggested expression. + self.assertIn("using typename T::TypeDef", + value.GetError().GetCString()) + self.assertIn("i->m", + value.GetError().GetCString()) + + # Retry twice, which will get the expression working. + multiple_runs_options.SetRetriesWithFixIts(2) + value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options) + # This error signals success for top level expressions. + self.assertEquals(value.GetError().GetCString(), "error: No value") + + # Test that the code above compiles to the right thing. + self.expect_expr("test_X(1)", result_type="int", result_value="123")