This commit improves upon cc0b5ebf7f, which added support for
specifying which libcxx to use when testing LLDB. That patch honored
requests by tests that had `USE_LIBCPP=1` defined in their makefiles.
Now, we also use a non-default libcxx if all conditions below are true:
1. The test is not explicitly requesting the use of libstdcpp
(USE_LIBSTDCPP=1).
2. The test is not explicitly requesting the use of the system's
library (USE_SYSTEM_STDLIB=1).
3. A path to libcxx was either provided by the user through CMake flags
or libcxx was built together with LLDB.
Condition (2) is new and introduced in this patch in order to support
tests that are either:
* Cross-platform (such as API/macosx/macCatalyst and
API/tools/lldb-server). The just-built libcxx is usually not built for
platforms other than the host's.
* Cross-language (such as API/lang/objc/exceptions). In this case, the
Objective C runtime throws an exceptions that always goes through the
system's libcxx, instead of the just built libcxx. Fixing this would
require either changing the install-name of the just built libcxx in Mac
systems, or tuning the DYLD_LIBRARY_PATH variable at runtime.
Some other tests exposes limitations of LLDB when running with a debug
standard library. TestDbgInfoContentForwardLists had an assertion
removed, as it was checking for buggy LLDB behavior (which now
crashes). TestFixIts had a variable renamed, as the old name clashes
with a standard library name when debug info is present. This is a known
issue: https://github.com/llvm/llvm-project/issues/34391.
For `TestSBModule`, the way the "main" module is found was changed to
look for the "a.out" module, instead of relying on the index being 0. In
some systems, the index 0 is dyld when a custom standard library is
used.
Differential Revision: https://reviews.llvm.org/D132940
177 lines
7.8 KiB
Python
177 lines
7.8 KiB
Python
"""
|
|
Test calling an expression with errors that a FixIt can fix.
|
|
"""
|
|
|
|
import lldb
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
|
|
|
|
class ExprCommandWithFixits(TestBase):
|
|
|
|
def test_with_dummy_target(self):
|
|
"""Test calling expressions in the dummy target with errors that can be fixed by the FixIts."""
|
|
|
|
# Enable fix-its as they were intentionally disabled by TestBase.setUp.
|
|
self.runCmd("settings set target.auto-apply-fixits true")
|
|
|
|
ret_val = lldb.SBCommandReturnObject()
|
|
result = self.dbg.GetCommandInterpreter().HandleCommand("expression ((1 << 16) - 1))", ret_val)
|
|
self.assertEqual(result, lldb.eReturnStatusSuccessFinishResult, ret_val.GetError())
|
|
self.assertIn("Fix-it applied", ret_val.GetError())
|
|
|
|
def test_with_target(self):
|
|
"""Test calling expressions with errors that can be fixed by the FixIts."""
|
|
self.build()
|
|
(target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
|
'Stop here to evaluate expressions',
|
|
lldb.SBFileSpec("main.cpp"))
|
|
|
|
options = lldb.SBExpressionOptions()
|
|
options.SetAutoApplyFixIts(True)
|
|
|
|
top_level_options = lldb.SBExpressionOptions()
|
|
top_level_options.SetAutoApplyFixIts(True)
|
|
top_level_options.SetTopLevel(True)
|
|
|
|
frame = self.thread.GetFrameAtIndex(0)
|
|
|
|
# Try with one error:
|
|
value = frame.EvaluateExpression("my_pointer.first", options)
|
|
self.assertTrue(value.IsValid())
|
|
self.assertSuccess(value.GetError())
|
|
self.assertEquals(value.GetValueAsUnsigned(), 10)
|
|
|
|
# Try with one error in a top-level expression.
|
|
# The Fix-It changes "ptr.m" to "ptr->m".
|
|
expr = "struct MyTy { int m; }; MyTy x; MyTy *ptr = &x; int m = ptr.m;"
|
|
value = frame.EvaluateExpression(expr, top_level_options)
|
|
# A successfully parsed top-level expression will yield an error
|
|
# that there is 'no value'. If a parsing error would have happened we
|
|
# would get a different error kind, so let's check the error kind here.
|
|
self.assertEquals(value.GetError().GetCString(), "error: No value")
|
|
|
|
# Try with two errors:
|
|
two_error_expression = "my_pointer.second->a"
|
|
value = frame.EvaluateExpression(two_error_expression, options)
|
|
self.assertTrue(value.IsValid())
|
|
self.assertSuccess(value.GetError())
|
|
self.assertEquals(value.GetValueAsUnsigned(), 20)
|
|
|
|
# Try a Fix-It that is stored in the 'note:' diagnostic of an error.
|
|
# The Fix-It here is adding parantheses around the ToStr parameters.
|
|
fixit_in_note_expr ="#define ToStr(x) #x\nToStr(0 {, })"
|
|
value = frame.EvaluateExpression(fixit_in_note_expr, options)
|
|
self.assertTrue(value.IsValid())
|
|
self.assertSuccess(value.GetError())
|
|
self.assertEquals(value.GetSummary(), '"(0 {, })"')
|
|
|
|
# Now turn off the fixits, and the expression should fail:
|
|
options.SetAutoApplyFixIts(False)
|
|
value = frame.EvaluateExpression(two_error_expression, options)
|
|
self.assertTrue(value.IsValid())
|
|
self.assertTrue(value.GetError().Fail())
|
|
error_string = value.GetError().GetCString()
|
|
self.assertTrue(
|
|
error_string.find("fixed expression suggested:") != -1,
|
|
"Fix was suggested")
|
|
self.assertTrue(
|
|
error_string.find("my_pointer->second.a") != -1,
|
|
"Fix was right")
|
|
|
|
def test_with_target_error_applies_fixit(self):
|
|
""" Check that applying a Fix-it which fails to execute correctly still
|
|
prints that the Fix-it was applied. """
|
|
self.build()
|
|
(target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
|
'Stop here to evaluate expressions',
|
|
lldb.SBFileSpec("main.cpp"))
|
|
# Enable fix-its as they were intentionally disabled by TestBase.setUp.
|
|
self.runCmd("settings set target.auto-apply-fixits true")
|
|
ret_val = lldb.SBCommandReturnObject()
|
|
result = self.dbg.GetCommandInterpreter().HandleCommand("expression null_pointer.first", ret_val)
|
|
self.assertEqual(result, lldb.eReturnStatusFailed, ret_val.GetError())
|
|
|
|
self.assertIn("Fix-it applied, fixed expression was:", ret_val.GetError())
|
|
self.assertIn("null_pointer->first", ret_val.GetError())
|
|
|
|
# The final function call runs into SIGILL on aarch64-linux.
|
|
@expectedFailureAll(archs=["aarch64"], oslist=["freebsd", "linux"],
|
|
bugnumber="llvm.org/pr49407")
|
|
def test_with_multiple_retries(self):
|
|
"""Test calling expressions with errors that can be fixed by the FixIts."""
|
|
self.build()
|
|
(target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
|
'Stop here to evaluate expressions',
|
|
lldb.SBFileSpec("main.cpp"))
|
|
|
|
# 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)
|
|
|
|
frame = self.thread.GetFrameAtIndex(0)
|
|
|
|
# An expression that needs two parse attempts with one Fix-It each
|
|
# to be successfully parsed.
|
|
two_runs_expr = """
|
|
struct Data { int m; };
|
|
|
|
template<typename T>
|
|
struct S1 : public T {
|
|
using T::TypeDef;
|
|
int f() {
|
|
Data data;
|
|
data.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<T>::TypeDef i = &data;
|
|
// 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<ClassWithTypeDef> s1;
|
|
return s1.f();
|
|
}
|
|
"""
|
|
|
|
# Disable retries which will fail.
|
|
multiple_runs_options.SetRetriesWithFixIts(0)
|
|
value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options)
|
|
errmsg = value.GetError().GetCString()
|
|
self.assertIn("expression failed to parse", errmsg)
|
|
self.assertIn("using declaration resolved to type without 'typename'",
|
|
errmsg)
|
|
self.assertIn("fixed expression suggested:", errmsg)
|
|
self.assertIn("using typename T::TypeDef", errmsg)
|
|
# The second Fix-It shouldn't be suggested here as Clang should have
|
|
# aborted the parsing process.
|
|
self.assertNotIn("i->m", errmsg)
|
|
|
|
# Retry once, but the expression needs two retries.
|
|
multiple_runs_options.SetRetriesWithFixIts(1)
|
|
value = frame.EvaluateExpression(two_runs_expr, multiple_runs_options)
|
|
errmsg = value.GetError().GetCString()
|
|
self.assertIn("expression failed to parse", errmsg)
|
|
self.assertIn("fixed expression suggested:", errmsg)
|
|
# Both our fixed expressions should be in the suggested expression.
|
|
self.assertIn("using typename T::TypeDef", errmsg)
|
|
self.assertIn("i->m", errmsg)
|
|
|
|
# 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")
|