diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -173,6 +173,8 @@ llvm::StringRef GetExpressionPrefixContents(); + uint64_t GetExprErrorLimit() const; + bool GetUseHexImmediates() const; bool GetUseFastStepping() const; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -454,6 +454,10 @@ // 4. Create and install the target on the compiler. m_compiler->createDiagnostics(); + // Limit the number of error diagnostics we emit. + // A value of 0 means no limit for both LLDB and Clang. + m_compiler->getDiagnostics().setErrorLimit(target_sp->GetExprErrorLimit()); + auto target_info = TargetInfo::CreateTargetInfo( m_compiler->getDiagnostics(), m_compiler->getInvocation().TargetOpts); if (log) { diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4026,6 +4026,12 @@ return ""; } +uint64_t TargetProperties::GetExprErrorLimit() const { + const uint32_t idx = ePropertyExprErrorLimit; + return m_collection_sp->GetPropertyAtIndexAsUInt64( + nullptr, idx, g_target_properties[idx].default_uint_value); +} + bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() { const uint32_t idx = ePropertyBreakpointUseAvoidList; return m_collection_sp->GetPropertyAtIndexAsBoolean( diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -20,6 +20,10 @@ def ExprPrefix: Property<"expr-prefix", "FileSpec">, DefaultStringValue<"">, Desc<"Path to a file containing expressions to be prepended to all expressions.">; + def ExprErrorLimit: Property<"expr-error-limit", "UInt64">, + DefaultUnsignedValue<5>, + Desc<"The maximum amount of errors to emit while parsing an expression. " + "A value of 0 means to always continue parsing if possible.">; def PreferDynamic: Property<"prefer-dynamic-value", "Enum">, DefaultEnumValue<"eDynamicDontRunTarget">, EnumValues<"OptionEnumValues(g_dynamic_value_types)">, diff --git a/lldb/test/API/commands/expression/error-limit/Makefile b/lldb/test/API/commands/expression/error-limit/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/expression/error-limit/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/expression/error-limit/TestExprErrorLimit.py b/lldb/test/API/commands/expression/error-limit/TestExprErrorLimit.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/expression/error-limit/TestExprErrorLimit.py @@ -0,0 +1,60 @@ +""" +Tests target.expr-error-limit. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test(self): + # FIXME: The only reason this test needs to create a real target is because + # the settings of the dummy target can't be changed with `settings set`. + self.build() + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + # Our test expression that is just several lines of malformed + # integer literals (with a 'yerror' integer suffix). Every error + # has its own unique string (1, 2, 3, 4) and is on its own line + # that we can later find it when Clang prints the respective source + # code for each error to the error output. + # For example, in the error output below we would look for the + # unique `1yerror` string: + # error: :1:2: invalid suffix 'yerror' on integer constant + # 1yerror + # ^ + expr = "1yerror;\n2yerror;\n3yerror;\n4yerror;" + + options = lldb.SBExpressionOptions() + options.SetAutoApplyFixIts(False) + + # Evaluate the expression and check that only the first 2 errors are + # emitted. + self.runCmd("settings set target.expr-error-limit 2") + eval_result = target.EvaluateExpression(expr, options) + self.assertIn("1yerror", str(eval_result.GetError())) + self.assertIn("2yerror", str(eval_result.GetError())) + self.assertNotIn("3yerror", str(eval_result.GetError())) + self.assertNotIn("4yerror", str(eval_result.GetError())) + + # Change to a 3 errors and check again which errors are emitted. + self.runCmd("settings set target.expr-error-limit 3") + eval_result = target.EvaluateExpression(expr, options) + self.assertIn("1yerror", str(eval_result.GetError())) + self.assertIn("2yerror", str(eval_result.GetError())) + self.assertIn("3yerror", str(eval_result.GetError())) + self.assertNotIn("4yerror", str(eval_result.GetError())) + + # Disable the error limit and make sure all errors are emitted. + self.runCmd("settings set target.expr-error-limit 0") + eval_result = target.EvaluateExpression(expr, options) + self.assertIn("1yerror", str(eval_result.GetError())) + self.assertIn("2yerror", str(eval_result.GetError())) + self.assertIn("3yerror", str(eval_result.GetError())) + self.assertIn("4yerror", str(eval_result.GetError())) diff --git a/lldb/test/API/commands/expression/error-limit/main.cpp b/lldb/test/API/commands/expression/error-limit/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/commands/expression/error-limit/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; // break here +}