Skip to content

Commit 0ba42f1

Browse files
authored
Merge pull request #3339 from Teemperor/cherry/6fcb857746c19b5ed46afdf732b839082326f9d4
[lldb][import-std-module] Prefer the non-module diagnostics when in f…
2 parents 67f6723 + 6a392bc commit 0ba42f1

File tree

9 files changed

+111
-14
lines changed

9 files changed

+111
-14
lines changed

lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,15 +689,22 @@ bool ClangUserExpression::Parse(DiagnosticManager &diagnostic_manager,
689689
SetupCppModuleImports(exe_ctx);
690690
// If we did load any modules, then retry parsing.
691691
if (!m_imported_cpp_modules.empty()) {
692+
// Create a dedicated diagnostic manager for the second parse attempt.
693+
// These diagnostics are only returned to the caller if using the fallback
694+
// actually succeeded in getting the expression to parse. This prevents
695+
// that module-specific issues regress diagnostic quality with the
696+
// fallback mode.
697+
DiagnosticManager retry_manager;
692698
// The module imports are injected into the source code wrapper,
693699
// so recreate those.
694-
CreateSourceCode(diagnostic_manager, exe_ctx, m_imported_cpp_modules,
700+
CreateSourceCode(retry_manager, exe_ctx, m_imported_cpp_modules,
695701
/*for_completion*/ false);
696-
// Clear the error diagnostics from the previous parse attempt.
697-
diagnostic_manager.Clear();
698-
parse_success = TryParse(diagnostic_manager, exe_scope, exe_ctx,
702+
parse_success = TryParse(retry_manager, exe_scope, exe_ctx,
699703
execution_policy, keep_result_in_memory,
700704
generate_debug_info);
705+
// Return the parse diagnostics if we were successful.
706+
if (parse_success)
707+
diagnostic_manager = std::move(retry_manager);
701708
}
702709
}
703710
if (!parse_success)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# We don't have any standard include directories, so we can't
2+
# parse the test_common.h header we usually inject as it includes
3+
# system headers.
4+
NO_TEST_COMMON_H := 1
5+
6+
CXXFLAGS_EXTRAS = -I $(SRCDIR)/root/usr/include/c++/v1/ -I $(SRCDIR)/root/usr/include/ -nostdinc -nostdinc++ -DBUILDING_OUTSIDE_LLDB=1
7+
CXX_SOURCES := main.cpp
8+
9+
include Makefile.rules
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
Tests that the import-std-module=fallback setting is only showing the error
3+
diagnostics from the first parse attempt which isn't using a module.
4+
This is supposed to prevent that a broken libc++ module renders failing
5+
expressions useless as the original failing errors are suppressed by the
6+
module build errors.
7+
"""
8+
9+
from lldbsuite.test.decorators import *
10+
from lldbsuite.test.lldbtest import *
11+
from lldbsuite.test import lldbutil
12+
import os
13+
14+
15+
class TestCase(TestBase):
16+
17+
mydir = TestBase.compute_mydir(__file__)
18+
19+
# We only emulate a fake libc++ in this test and don't use the real libc++,
20+
# but we still add the libc++ category so that this test is only run in
21+
# test configurations where libc++ is actually supposed to be tested.
22+
@add_test_categories(["libc++"])
23+
@skipIfRemote
24+
@skipIf(compiler=no_match("clang"))
25+
def test(self):
26+
self.build()
27+
28+
sysroot = os.path.join(os.getcwd(), "root")
29+
30+
# Set the sysroot this test is using to provide a custom libc++.
31+
self.runCmd("platform select --sysroot '" + sysroot + "' host",
32+
CURRENT_EXECUTABLE_SET)
33+
34+
lldbutil.run_to_source_breakpoint(self,
35+
"// Set break point at this line.",
36+
lldb.SBFileSpec("main.cpp"))
37+
38+
# The expected error message when the fake libc++ module in this test
39+
# fails to build from within LLDB (as it contains invalid code).
40+
module_build_error_msg = "unknown type name 'random_token_to_fail_the_build'"
41+
42+
# First force the std module to be imported. This should show the
43+
# module build error to the user.
44+
self.runCmd("settings set target.import-std-module true")
45+
self.expect("expr (size_t)v.size()",
46+
substrs=[module_build_error_msg],
47+
error=True)
48+
49+
# In the fallback mode the module build error should not be shown.
50+
self.runCmd("settings set target.import-std-module fallback")
51+
fallback_expr = "expr v ; error_to_trigger_fallback_mode"
52+
# First check for the actual expression error that should be displayed
53+
# and is useful for the user.
54+
self.expect(fallback_expr,
55+
substrs=["use of undeclared identifier 'error_to_trigger_fallback_mode'"],
56+
error=True)
57+
# Test that the module build error is not displayed.
58+
self.expect(fallback_expr,
59+
substrs=[module_build_error_msg],
60+
matching=False,
61+
error=True)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <algorithm>
2+
3+
int main(int argc, char **argv) {
4+
// Makes sure we have the mock libc headers in the debug information.
5+
libc_struct s;
6+
std::vector<int> v;
7+
return 0; // Set break point at this line.
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This is only defined when building, but LLDB is missing this flag when loading the standard
2+
// library module so the actual contents of the module are missing.
3+
#ifdef BUILDING_OUTSIDE_LLDB
4+
5+
#include "stdio.h"
6+
7+
namespace std {
8+
inline namespace __1 {
9+
// Pretend to be a std::vector template we need to instantiate
10+
// in LLDB.
11+
template<typename T>
12+
struct vector { T i; int size() { return 2; } };
13+
}
14+
}
15+
#else
16+
// Break the build when parsing from within LLDB.
17+
random_token_to_fail_the_build
18+
#endif // BUILDING_OUTSIDE_LLDB
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module std {
2+
module "algorithm" { header "algorithm" export * }
3+
}

lldb/test/API/commands/expression/import-std-module/module-build-errors/root/usr/include/c++/v1/vector

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
struct libc_struct {};

lldb/test/API/commands/expression/import-std-module/retry-with-std-module/TestRetryWithStdModule.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,6 @@ def test(self):
4848
self.expect("expr --top-level -- int i = std::max(1, 2);", error=True,
4949
substrs=["no member named 'max' in namespace 'std'"])
5050

51-
# Check that diagnostics from the first parse attempt don't show up
52-
# in the C++ module parse attempt. In the expression below, we first
53-
# fail to parse 'std::max'. Then we retry with a loaded C++ module
54-
# and succeed to parse the 'std::max' part. However, the
55-
# trailing 'unknown_identifier' will fail to parse even with the
56-
# loaded module. The 'std::max' diagnostic from the first attempt
57-
# however should not be shown to the user.
58-
self.expect("expr std::max(1, 2); unknown_identifier", error=True,
59-
matching=False,
60-
substrs=["no member named 'max'"])
6151
# The proper diagnostic however should be shown on the retry.
6252
self.expect("expr std::max(1, 2); unknown_identifier", error=True,
6353
substrs=["use of undeclared identifier 'unknown_identifier'"])

0 commit comments

Comments
 (0)