Files
clang-p2996/lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py
Raphael Isemann 958608285e [lldb] Allow LLDB to automatically retry a failed expression with an imported std C++ module
By now LLDB can import the 'std' C++ module to improve expression evaluation,
but there are still a few problems to solve before we can do this by default.
One is that importing the C++ module is slightly slower than normal expression
evaluation (mostly because the disk access and loading the initial lookup data
is quite slow in comparison to the barebone Clang setup the rest of the LLDB
expression evaluator is usually doing). Another problem is that some complicated
types in the standard library aren't fully supported yet by the ASTImporter, so
we end up types that fail to import (which usually appears to the user as if the
type is empty or there is just no result variable).

To still allow people to adopt this mode in their daily debugging, this patch
adds a setting that allows LLDB to automatically retry failed expression with a
loaded C++ module. All success expressions will behave exactly as they would do
before this patch. Failed expressions get a another parse attempt if we find a
usable C++ module in the current execution context. This way we shouldn't have
any performance/parsing regressions in normal debugging workflows, while the
debugging workflows involving STL containers benefit from the C++ module type
info.

This setting is off by default for now with the intention to enable it by
default on macOS soon-ish.

The implementation is mostly just extracting the existing parse logic into its
own function and then calling the parse function again if the first evaluation
failed and we have a C++ module to retry the parsing with.

Reviewed By: shafik, JDevlieghere, aprantl

Differential Revision: https://reviews.llvm.org/D92784
2020-12-10 12:29:17 +01:00

77 lines
3.8 KiB
Python

from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
def test(self):
self.build()
lldbutil.run_to_source_breakpoint(self,
"// Set break point at this line.",
lldb.SBFileSpec("main.cpp"))
# Test printing the vector before enabling any C++ module setting.
self.expect_expr("a", result_type="std::vector<int, std::allocator<int> >")
# Set loading the import-std-module to 'fallback' which loads the module
# and retries when an expression fails to parse.
self.runCmd("settings set target.import-std-module fallback")
# Printing the vector still works. This should return the same type
# as before as this shouldn't use a C++ module type (the C++ module type
# is hiding the second template parameter as it's equal to the default
# argument which the C++ module has type info for).
self.expect_expr("a", result_type="std::vector<int, std::allocator<int> >")
# This expression can only parse with a C++ module. LLDB should
# automatically fall back to import the C++ module to get this working.
self.expect_expr("std::max<std::size_t>(0U, a.size())", result_value="3")
# The 'a' and 'local' part can be parsed without loading a C++ module and will
# load type/runtime information. The 'std::max...' part will fail to
# parse without a C++ module. Make sure we reset all the relevant parts of
# the C++ parser so that we don't end up with for example a second
# definition of 'local' when retrying.
self.expect_expr("a; local; std::max<std::size_t>(0U, a.size())", result_value="3")
# Try to declare top-level declarations that require a C++ module to parse.
# Top-level expressions don't support importing the C++ module (yet), so
# this should still fail as before.
self.expect("expr --top-level -- int i = std::max(1, 2);", error=True,
substrs=["no member named 'max' in namespace 'std'"])
# Check that diagnostics from the first parse attempt don't show up
# in the C++ module parse attempt. In the expression below, we first
# fail to parse 'std::max'. Then we retry with a loaded C++ module
# and succeed to parse the 'std::max' part. However, the
# trailing 'unknown_identifier' will fail to parse even with the
# loaded module. The 'std::max' diagnostic from the first attempt
# however should not be shown to the user.
self.expect("expr std::max(1, 2); unknown_identifier", error=True,
matching=False,
substrs=["no member named 'max'"])
# The proper diagnostic however should be shown on the retry.
self.expect("expr std::max(1, 2); unknown_identifier", error=True,
substrs=["use of undeclared identifier 'unknown_identifier'"])
# Turn on the 'import-std-module' setting and make sure we import the
# C++ module.
self.runCmd("settings set target.import-std-module true")
# This is still expected to work.
self.expect_expr("std::max<std::size_t>(0U, a.size())", result_value="3")
# Turn of the 'import-std-module' setting and make sure we don't load
# the module (which should prevent parsing the expression involving
# 'std::max').
self.runCmd("settings set target.import-std-module false")
self.expect("expr std::max(1, 2);", error=True,
substrs=["no member named 'max' in namespace 'std'"])