You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
9.5 KiB
237 lines
9.5 KiB
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test import lldbutil
|
|
|
|
"""
|
|
This test ensures that we only create Clang AST nodes in our module AST
|
|
when we actually need them.
|
|
|
|
All tests in this file behave like this:
|
|
1. Use LLDB to do something (expression evaluation, breakpoint setting, etc.).
|
|
2. Check that certain Clang AST nodes were not loaded during the previous
|
|
step.
|
|
"""
|
|
|
|
class TestCase(TestBase):
|
|
|
|
NO_DEBUG_INFO_TESTCASE = True
|
|
mydir = TestBase.compute_mydir(__file__)
|
|
|
|
def setUp(self):
|
|
TestBase.setUp(self)
|
|
# Only build this test once.
|
|
self.build()
|
|
|
|
# Clang declaration kind we are looking for.
|
|
class_decl_kind = "CXXRecordDecl"
|
|
# FIXME: This shouldn't be a CXXRecordDecl, but that's how we model
|
|
# structs in Clang.
|
|
struct_decl_kind = "CXXRecordDecl"
|
|
|
|
# The decls we use in this program in the format that
|
|
# decl_in_line and decl_completed_in_line expect (which is a pair of
|
|
# node type and the unqualified declaration name.
|
|
struct_first_member_decl = [struct_decl_kind, "StructFirstMember"]
|
|
struct_behind_ptr_decl = [struct_decl_kind, "StructBehindPointer"]
|
|
struct_behind_ref_decl = [struct_decl_kind, "StructBehindRef"]
|
|
struct_member_decl = [struct_decl_kind, "StructMember"]
|
|
some_struct_decl = [struct_decl_kind, "SomeStruct"]
|
|
other_struct_decl = [struct_decl_kind, "OtherStruct"]
|
|
class_in_namespace_decl = [class_decl_kind, "ClassInNamespace"]
|
|
class_we_enter_decl = [class_decl_kind, "ClassWeEnter"]
|
|
class_member_decl = [struct_decl_kind, "ClassMember"]
|
|
unused_class_member_decl = [struct_decl_kind, "UnusedClassMember"]
|
|
unused_class_member_ptr_decl = [struct_decl_kind, "UnusedClassMemberPtr"]
|
|
|
|
def assert_no_decls_loaded(self):
|
|
"""
|
|
Asserts that no known declarations in this test are loaded
|
|
into the module's AST.
|
|
"""
|
|
self.assert_decl_not_loaded(self.struct_first_member_decl)
|
|
self.assert_decl_not_loaded(self.struct_behind_ptr_decl)
|
|
self.assert_decl_not_loaded(self.struct_behind_ref_decl)
|
|
self.assert_decl_not_loaded(self.struct_member_decl)
|
|
self.assert_decl_not_loaded(self.some_struct_decl)
|
|
self.assert_decl_not_loaded(self.other_struct_decl)
|
|
self.assert_decl_not_loaded(self.class_in_namespace_decl)
|
|
self.assert_decl_not_loaded(self.class_member_decl)
|
|
self.assert_decl_not_loaded(self.unused_class_member_decl)
|
|
|
|
def get_ast_dump(self):
|
|
"""Returns the dumped Clang AST of the module as a string"""
|
|
res = lldb.SBCommandReturnObject()
|
|
ci = self.dbg.GetCommandInterpreter()
|
|
ci.HandleCommand('target modules dump ast a.out', res)
|
|
self.assertTrue(res.Succeeded())
|
|
return res.GetOutput()
|
|
|
|
def decl_in_line(self, line, decl):
|
|
"""
|
|
Returns true iff the given line declares the given Clang decl.
|
|
The line is expected to be in the form of Clang's AST dump.
|
|
"""
|
|
line = line.rstrip() + "\n"
|
|
decl_kind = "-" + decl[0] + " "
|
|
# Either the decl is somewhere in the line or at the end of
|
|
# the line.
|
|
decl_name = " " + decl[1] + " "
|
|
decl_name_eol = " " + decl[1] + "\n"
|
|
if not decl_kind in line:
|
|
return False
|
|
return decl_name in line or decl_name_eol in line
|
|
|
|
def decl_completed_in_line(self, line, decl):
|
|
"""
|
|
Returns true iff the given line declares the given Clang decl and
|
|
the decl was completed (i.e., it has no undeserialized declarations
|
|
in it).
|
|
"""
|
|
return self.decl_in_line(line, decl) and not "<undeserialized declarations>" in line
|
|
|
|
# The following asserts are used for checking if certain Clang declarations
|
|
# were loaded or not since the target was created.
|
|
|
|
def assert_decl_loaded(self, decl):
|
|
"""
|
|
Asserts the given decl is currently loaded.
|
|
Note: This test is about checking that types/declarations are not
|
|
loaded. If this assert fails it is usually fine to turn it into a
|
|
assert_decl_not_loaded or assert_decl_not_completed assuming LLDB's
|
|
functionality has not suffered by not loading this declaration.
|
|
"""
|
|
ast = self.get_ast_dump()
|
|
found = False
|
|
for line in ast.splitlines():
|
|
if self.decl_in_line(line, decl):
|
|
found = True
|
|
self.assertTrue(self.decl_completed_in_line(line, decl),
|
|
"Should have called assert_decl_not_completed")
|
|
self.assertTrue(found, "Declaration no longer loaded " + str(decl) +
|
|
".\nAST:\n" + ast)
|
|
|
|
def assert_decl_not_completed(self, decl):
|
|
"""
|
|
Asserts that the given decl is currently not completed in the module's
|
|
AST. It may be loaded but then can can only contain undeserialized
|
|
declarations.
|
|
"""
|
|
ast = self.get_ast_dump()
|
|
found = False
|
|
for line in ast.splitlines():
|
|
error_msg = "Unexpected completed decl: '" + line + "'.\nAST:\n" + ast
|
|
self.assertFalse(self.decl_completed_in_line(line, decl), error_msg)
|
|
|
|
def assert_decl_not_loaded(self, decl):
|
|
"""
|
|
Asserts that the given decl is currently not loaded in the module's
|
|
AST.
|
|
"""
|
|
ast = self.get_ast_dump()
|
|
found = False
|
|
for line in ast.splitlines():
|
|
error_msg = "Unexpected loaded decl: '" + line + "'\nAST:\n" + ast
|
|
self.assertFalse(self.decl_in_line(line, decl), error_msg)
|
|
|
|
|
|
def clean_setup(self, location):
|
|
"""
|
|
Runs to the line with the source line with the given location string
|
|
and ensures that our module AST is empty.
|
|
"""
|
|
lldbutil.run_to_source_breakpoint(self,
|
|
"// Location: " + location, lldb.SBFileSpec("main.cpp"))
|
|
# Make sure no declarations are loaded initially.
|
|
self.assert_no_decls_loaded()
|
|
|
|
@add_test_categories(["dwarf"])
|
|
def test_arithmetic_expression_in_main(self):
|
|
""" Runs a simple arithmetic expression which should load nothing. """
|
|
self.clean_setup(location="multiple locals function")
|
|
|
|
self.expect("expr 1 + (int)2.0", substrs=['(int) $0'])
|
|
|
|
# This should not have loaded any decls.
|
|
self.assert_no_decls_loaded()
|
|
|
|
@add_test_categories(["dwarf"])
|
|
def test_printing_local_variable_in_other_struct_func(self):
|
|
"""
|
|
Prints a local variable and makes sure no unrelated types are loaded.
|
|
"""
|
|
self.clean_setup(location="other struct function")
|
|
|
|
self.expect("expr other_struct_var", substrs=['(OtherStruct) $0'])
|
|
# The decl we run on was loaded.
|
|
self.assert_decl_loaded(self.other_struct_decl)
|
|
|
|
# This should not have loaded anything else.
|
|
self.assert_decl_not_loaded(self.some_struct_decl)
|
|
self.assert_decl_not_loaded(self.class_in_namespace_decl)
|
|
|
|
@add_test_categories(["dwarf"])
|
|
def test_printing_struct_with_multiple_locals(self):
|
|
"""
|
|
Prints a local variable and checks that we don't load other local
|
|
variables.
|
|
"""
|
|
self.clean_setup(location="multiple locals function")
|
|
|
|
self.expect("expr struct_var", substrs=['(SomeStruct) $0'])
|
|
|
|
# We loaded SomeStruct and its member types for printing.
|
|
self.assert_decl_loaded(self.some_struct_decl)
|
|
self.assert_decl_loaded(self.struct_behind_ptr_decl)
|
|
self.assert_decl_loaded(self.struct_behind_ref_decl)
|
|
|
|
# FIXME: We don't use these variables, but we seem to load all local
|
|
# local variables.
|
|
self.assert_decl_not_completed(self.other_struct_decl)
|
|
self.assert_decl_not_completed(self.class_in_namespace_decl)
|
|
|
|
@add_test_categories(["dwarf"])
|
|
def test_addr_of_struct(self):
|
|
"""
|
|
Prints the address of a local variable (which is a struct).
|
|
"""
|
|
self.clean_setup(location="multiple locals function")
|
|
|
|
self.expect("expr &struct_var", substrs=['(SomeStruct *) $0'])
|
|
|
|
# We loaded SomeStruct.
|
|
self.assert_decl_loaded(self.some_struct_decl)
|
|
|
|
# The member declarations should not be completed.
|
|
self.assert_decl_not_completed(self.struct_behind_ptr_decl)
|
|
self.assert_decl_not_completed(self.struct_behind_ref_decl)
|
|
|
|
# FIXME: The first member was behind a pointer so it shouldn't be
|
|
# completed. Somehow LLDB really wants to load the first member, so
|
|
# that is why have it defined here.
|
|
self.assert_decl_loaded(self.struct_first_member_decl)
|
|
|
|
# FIXME: We don't use these variables, but we seem to load all local
|
|
# local variables.
|
|
self.assert_decl_not_completed(self.other_struct_decl)
|
|
self.assert_decl_not_completed(self.class_in_namespace_decl)
|
|
|
|
@add_test_categories(["dwarf"])
|
|
def test_class_function_access_member(self):
|
|
self.clean_setup(location="class function")
|
|
|
|
self.expect("expr member", substrs=['(ClassMember) $0'])
|
|
|
|
# We loaded the current class we touched.
|
|
self.assert_decl_loaded(self.class_we_enter_decl)
|
|
# We loaded the unused members of this class.
|
|
self.assert_decl_loaded(self.unused_class_member_decl)
|
|
self.assert_decl_not_completed(self.unused_class_member_ptr_decl)
|
|
# We loaded the member we used.
|
|
self.assert_decl_loaded(self.class_member_decl)
|
|
|
|
# This should not have loaded anything else.
|
|
self.assert_decl_not_loaded(self.other_struct_decl)
|
|
self.assert_decl_not_loaded(self.some_struct_decl)
|
|
self.assert_decl_not_loaded(self.class_in_namespace_decl)
|
|
|